├── .commitlintrc ├── .github └── workflows │ └── release.yml ├── .gitignore ├── .release-it.ts ├── .vscode ├── extensions.json └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── eslint.config.ts ├── index.html ├── package.json ├── patches └── pixi-live2d-display.patch ├── pnpm-lock.yaml ├── public ├── images │ ├── backgrounds │ │ ├── keyboard.png │ │ └── standard.png │ └── logo.png ├── js │ ├── live2d.min.js │ └── live2dcubismcore.min.js └── models │ ├── keyboard │ ├── cat.model3.json │ ├── demomodel2.1024 │ │ ├── texture_00.png │ │ ├── texture_01.png │ │ └── texture_02.png │ ├── demomodel2.cdi3.json │ ├── demomodel2.moc3 │ ├── exp_1.exp3.json │ ├── exp_2.exp3.json │ ├── live2d_expression0.exp3.json │ ├── live2d_expression1.exp3.json │ ├── live2d_expression2.exp3.json │ ├── live2d_motion1.flac │ ├── live2d_motion1.motion3.json │ └── live2d_motion2.motion3.json │ └── standard │ ├── cat.model3.json │ ├── demomodel.1024 │ ├── texture_00.png │ ├── texture_01.png │ └── texture_02.png │ ├── demomodel.cdi3.json │ ├── demomodel.moc3 │ ├── exp_1.exp3.json │ ├── exp_2.exp3.json │ ├── live2d_expression0.exp3.json │ ├── live2d_expression1.exp3.json │ ├── live2d_expression2.exp3.json │ ├── live2d_motion1.flac │ ├── live2d_motion1.motion3.json │ └── live2d_motion2.motion3.json ├── scripts ├── buildIcon.ts └── release.ts ├── src-tauri ├── .gitignore ├── BongoCat.desktop ├── Cargo.lock ├── Cargo.toml ├── assets │ ├── logo-mac.png │ ├── logo.png │ └── tray.png ├── build.rs ├── capabilities │ └── default.json ├── src │ ├── core │ │ ├── device.rs │ │ ├── mod.rs │ │ ├── prevent_default.rs │ │ └── setup │ │ │ ├── linux.rs │ │ │ ├── macos.rs │ │ │ ├── mod.rs │ │ │ └── windows.rs │ ├── lib.rs │ ├── main.rs │ └── plugins │ │ └── window │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── permissions │ │ └── default.toml │ │ └── src │ │ ├── commands │ │ ├── macos.rs │ │ ├── mod.rs │ │ └── not_macos.rs │ │ └── lib.rs ├── tauri.conf.json ├── tauri.linux.conf.json ├── tauri.macos.conf.json └── tauri.windows.conf.json ├── src ├── App.vue ├── assets │ ├── css │ │ └── global.scss │ └── images │ │ └── keys │ │ ├── Alt.png │ │ ├── AltGr.png │ │ ├── BackQuote.png │ │ ├── Backspace.png │ │ ├── CapsLock.png │ │ ├── Control.png │ │ ├── ControlLeft.png │ │ ├── ControlRight.png │ │ ├── Delete.png │ │ ├── DownArrow.png │ │ ├── Escape.png │ │ ├── Fn.png │ │ ├── KeyA.png │ │ ├── KeyB.png │ │ ├── KeyC.png │ │ ├── KeyD.png │ │ ├── KeyE.png │ │ ├── KeyF.png │ │ ├── KeyG.png │ │ ├── KeyH.png │ │ ├── KeyI.png │ │ ├── KeyJ.png │ │ ├── KeyK.png │ │ ├── KeyL.png │ │ ├── KeyM.png │ │ ├── KeyN.png │ │ ├── KeyO.png │ │ ├── KeyP.png │ │ ├── KeyQ.png │ │ ├── KeyR.png │ │ ├── KeyS.png │ │ ├── KeyT.png │ │ ├── KeyU.png │ │ ├── KeyV.png │ │ ├── KeyW.png │ │ ├── KeyX.png │ │ ├── KeyY.png │ │ ├── KeyZ.png │ │ ├── LeftArrow.png │ │ ├── Meta.png │ │ ├── Num0.png │ │ ├── Num1.png │ │ ├── Num2.png │ │ ├── Num3.png │ │ ├── Num4.png │ │ ├── Num5.png │ │ ├── Num6.png │ │ ├── Num7.png │ │ ├── Num8.png │ │ ├── Num9.png │ │ ├── Return.png │ │ ├── RightArrow.png │ │ ├── Shift.png │ │ ├── ShiftLeft.png │ │ ├── ShiftRight.png │ │ ├── Slash.png │ │ ├── Space.png │ │ ├── Tab.png │ │ └── UpArrow.png ├── components │ ├── pro-list-item │ │ └── index.vue │ ├── pro-list │ │ └── index.vue │ └── update-app │ │ └── index.vue ├── composables │ ├── useDevice.ts │ ├── useModel.ts │ ├── useTauriListen.ts │ ├── useThemeVars.ts │ ├── useTray.ts │ └── useWindowState.ts ├── constants │ └── index.ts ├── layouts │ └── preference │ │ └── index.vue ├── main.ts ├── pages │ ├── about │ │ └── index.vue │ ├── cat │ │ └── index.vue │ ├── general │ │ └── index.vue │ ├── main │ │ └── index.vue │ └── model │ │ └── index.vue ├── plugins │ └── window.ts ├── router │ └── index.ts ├── stores │ ├── app.ts │ ├── cat.ts │ ├── general.ts │ └── model.ts ├── utils │ ├── live2d.ts │ ├── monitor.ts │ ├── platform.ts │ └── tauri.ts └── vite-env.d.ts ├── static └── demo.gif ├── tsconfig.json ├── tsconfig.node.json ├── uno.config.ts └── vite.config.ts /.commitlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@commitlint/config-conventional" 3 | } 4 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release CI 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | create-release: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | - name: Set output 17 | id: vars 18 | run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT 19 | 20 | - name: Setup node 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: 20 24 | 25 | - name: Generate changelog 26 | id: create_release 27 | run: npx changelogithub --draft --name ${{ steps.vars.outputs.tag }} 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} 30 | 31 | build-app: 32 | needs: create-release 33 | permissions: 34 | contents: write 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | include: 39 | - platform: macos-latest 40 | target: aarch64-apple-darwin 41 | - platform: macos-latest 42 | target: x86_64-apple-darwin 43 | 44 | - platform: windows-latest 45 | target: x86_64-pc-windows-msvc 46 | - platform: windows-latest 47 | target: i686-pc-windows-msvc 48 | - platform: windows-latest 49 | target: aarch64-pc-windows-msvc 50 | 51 | - platform: ubuntu-22.04 52 | target: x86_64-unknown-linux-gnu 53 | 54 | runs-on: ${{ matrix.platform }} 55 | steps: 56 | - name: Checkout repository 57 | uses: actions/checkout@v4 58 | - name: Setup node 59 | uses: actions/setup-node@v4 60 | with: 61 | node-version: 20 62 | - uses: pnpm/action-setup@v3 63 | with: 64 | version: latest 65 | 66 | - name: Install rust target 67 | run: rustup target add ${{ matrix.target }} 68 | 69 | - name: Install dependencies (ubuntu only) 70 | if: matrix.platform == 'ubuntu-22.04' 71 | run: | 72 | sudo apt-get update 73 | sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf 74 | 75 | - name: Install Rust stable 76 | uses: dtolnay/rust-toolchain@stable 77 | 78 | - name: Rust cache 79 | uses: swatinem/rust-cache@v2 80 | with: 81 | workspaces: target 82 | 83 | - name: Sync node version and setup cache 84 | uses: actions/setup-node@v4 85 | with: 86 | node-version: 20 87 | cache: pnpm 88 | 89 | - name: Install app dependencies and build web 90 | run: pnpm install --frozen-lockfile 91 | 92 | - name: Build the app 93 | uses: tauri-apps/tauri-action@v0 94 | env: 95 | CI: false 96 | PLATFORM: ${{ matrix.platform }} 97 | GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} 98 | TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} 99 | TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} 100 | with: 101 | tagName: ${{ github.ref_name }} 102 | releaseName: BongoCat ${{ needs.create-release.outputs.APP_VERSION }} 103 | releaseBody: '' 104 | releaseDraft: true 105 | prerelease: false 106 | args: --target ${{ matrix.target }} 107 | -------------------------------------------------------------------------------- /.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 | *.local 14 | target 15 | 16 | # Editor directories and files 17 | .idea 18 | .DS_Store 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /.release-it.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-template-curly-in-string */ 2 | import type { Config } from 'release-it' 3 | 4 | export default { 5 | git: { 6 | commitMessage: 'v${version}', 7 | tagName: 'v${version}', 8 | }, 9 | npm: { 10 | publish: false, 11 | }, 12 | hooks: { 13 | 'after:bump': 'tsx scripts/release.ts', 14 | }, 15 | } satisfies Config 16 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "tauri-apps.tauri-vscode", 4 | "rust-lang.rust-analyzer", 5 | "antfu.unocss", 6 | "dbaeumer.vscode-eslint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Disable the default formatter, use eslint instead 3 | "prettier.enable": false, 4 | 5 | // Auto fix 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": "explicit", 8 | "source.organizeImports": "never" 9 | }, 10 | 11 | // Silent the stylistic rules in you IDE, but still auto fix them 12 | "eslint.rules.customizations": [ 13 | { "rule": "style/*", "severity": "off", "fixable": true }, 14 | { "rule": "format/*", "severity": "off", "fixable": true }, 15 | { "rule": "*-indent", "severity": "off", "fixable": true }, 16 | { "rule": "*-spacing", "severity": "off", "fixable": true }, 17 | { "rule": "*-spaces", "severity": "off", "fixable": true }, 18 | { "rule": "*-order", "severity": "off", "fixable": true }, 19 | { "rule": "*-dangle", "severity": "off", "fixable": true }, 20 | { "rule": "*-newline", "severity": "off", "fixable": true }, 21 | { "rule": "*quotes", "severity": "off", "fixable": true }, 22 | { "rule": "*semi", "severity": "off", "fixable": true } 23 | ], 24 | 25 | // Enable eslint for all supported languages 26 | "eslint.validate": [ 27 | "javascript", 28 | "javascriptreact", 29 | "typescript", 30 | "typescriptreact", 31 | "vue", 32 | "html", 33 | "markdown", 34 | "json", 35 | "json5", 36 | "jsonc", 37 | "yaml", 38 | "toml", 39 | "xml", 40 | "gql", 41 | "graphql", 42 | "astro", 43 | "svelte", 44 | "css", 45 | "less", 46 | "scss", 47 | "pcss", 48 | "postcss" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ "src-tauri" ] 4 | 5 | [workspace.dependencies] 6 | tauri = "2" 7 | serde = "1" 8 | serde_json = "1" 9 | fs_extra = "1" 10 | tauri-plugin = { version = "2", features = [ "build" ] } 11 | tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" } 12 | tauri-plugin-custom-window = { path = "./src-tauri/src/plugins/window" } 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 ayangweb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![BongoCat](https://socialify.git.ci/ayangweb/BongoCat/image?description=1&font=Source+Code+Pro&forks=1&issues=1&logo=https%3A%2F%2Fgithub.com%2Fayangweb%2FBongoCat%2Fblob%2Fcf4060302cab48e7567e19d1df62ad83b89edb7c%2Fsrc-tauri%2Fassets%2Flogo.png%3Fraw%3Dtrue&name=1&owner=1&pattern=Floating+Cogs&pulls=1&stargazers=1&theme=Light) 2 | 3 |
4 |
5 | 6 | Windows 10 | 11 | 12 | MacOS 16 | 17 | 18 | Linux 22 | 23 |
24 | 25 |
26 | 27 | 30 | 31 | 32 | 35 | 36 | 37 | 40 | 41 |
42 |
43 | 44 | ## 灵感来源 45 | 46 | 本项目灵感来源于 [MMmmmoko](https://github.com/MMmmmoko) 大佬的 [Bongo Cat Mver](https://github.com/MMmmmoko/Bongo-Cat-Mver)。由于原项目仅支持 Windows,作为一名深度 macOS 用户,我希望在自己的设备上也能使用这款可爱的 Bongo Cat,因此决定开发一个适配 macOS 的版本。 47 | 48 | 此外,得益于 Tauri 框架强大的跨平台能力,本项目不仅支持 macOS,还可在 Windows 和 Linux 上运行,让更多用户都能与这只可爱的猫咪互动! 49 | 50 | ![demo.gif](./static/demo.gif) 51 | 52 | ## 下载 53 | 54 | 请前往 [Releases](https://github.com/ayangweb/BongoCat/releases) 页面,下载最新版本。 55 | 56 | ### macOS 57 | 58 | - Apple Silicon:下载 `BongoCat_aarch64.dmg` 59 | - Intel Chip:下载 `BongoCat_x64.dmg` 60 | 61 | ### Windows 62 | 63 | - 64 位系统:下载 `BongoCat_x64.exe` 64 | - 32 位系统:下载 `BongoCat_x86.exe` 65 | - ARM64 架构:下载 `BongoCat_arm64.exe` 66 | 67 | ### Linux (X11) 68 | 69 | - Debian / Ubuntu:下载 `BongoCat_amd64.deb` 70 | - Fedora / RHEL:下载 `BongoCat_x86_64.rpm` 71 | - 通用版本:下载 `BongoCat_amd64.AppImage` 72 | 73 | ## 历史星标 74 | 75 | 76 | 77 | 78 | 79 | Star History Chart 80 | 81 | 82 | -------------------------------------------------------------------------------- /eslint.config.ts: -------------------------------------------------------------------------------- 1 | import antfu from '@antfu/eslint-config' 2 | 3 | export default antfu({ 4 | formatters: true, 5 | unocss: true, 6 | rules: { 7 | 'antfu/if-newline': 'off', 8 | 'style/brace-style': ['error', '1tbs'], 9 | 'ts/no-use-before-define': 'off', 10 | 'unused-imports/no-unused-imports': 'error', 11 | 'perfectionist/sort-imports': 'off', 12 | 'import/order': [ 13 | 'error', 14 | { 15 | 'newlines-between': 'always', 16 | 'groups': ['type', 'builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object'], 17 | 'alphabetize': { 18 | order: 'asc', 19 | caseInsensitive: true, 20 | }, 21 | }, 22 | ], 23 | 'vue/attributes-order': ['error', { alphabetical: true }], 24 | }, 25 | ignores: ['**/*.toml'], 26 | }) 27 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | BongoCat 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bongo-cat", 3 | "type": "module", 4 | "version": "0.2.1", 5 | "private": true, 6 | "author": { 7 | "name": "ayangweb", 8 | "email": "ayangweb@foxmail.com" 9 | }, 10 | "scripts": { 11 | "dev": "run-s build:icon dev:vite", 12 | "build": "run-s build:*", 13 | "dev:vite": "vite", 14 | "build:vite": "vite build", 15 | "build:icon": "tsx scripts/buildIcon.ts", 16 | "preview": "vite preview", 17 | "tauri": "tauri", 18 | "lint": "eslint --fix src", 19 | "preinstall": "npx only-allow pnpm", 20 | "prepare": "simple-git-hooks", 21 | "release": "release-it" 22 | }, 23 | "dependencies": { 24 | "@tauri-apps/api": "^2.5.0", 25 | "@tauri-apps/plugin-log": "~2.3.1", 26 | "@tauri-apps/plugin-opener": "~2.2.6", 27 | "@tauri-apps/plugin-os": "^2.2.1", 28 | "@tauri-apps/plugin-process": "^2.2.1", 29 | "@tauri-apps/plugin-updater": "~2.7.1", 30 | "@tauri-store/pinia": "^3.3.0", 31 | "@vueuse/core": "^13.1.0", 32 | "ant-design-vue": "^4.2.6", 33 | "dayjs": "^1.11.13", 34 | "is-url": "^1.2.4", 35 | "pinia": "^3.0.2", 36 | "pixi-live2d-display": "^0.4.0", 37 | "pixi.js": "^6.5.10", 38 | "radash": "^12.1.0", 39 | "vue": "^3.5.13", 40 | "vue-markdown-render": "^2.2.1", 41 | "vue-router": "^4.5.0" 42 | }, 43 | "devDependencies": { 44 | "@antfu/eslint-config": "^4.12.0", 45 | "@commitlint/cli": "^19.8.0", 46 | "@commitlint/config-conventional": "^19.8.0", 47 | "@iconify-json/solar": "^1.2.2", 48 | "@tauri-apps/cli": "^2.5.0", 49 | "@types/is-url": "^1.2.32", 50 | "@types/node": "^22.14.1", 51 | "@unocss/eslint-plugin": "^66.0.0", 52 | "@vitejs/plugin-vue": "^5.2.3", 53 | "eslint": "^9.25.1", 54 | "eslint-plugin-format": "^1.0.1", 55 | "lint-staged": "^15.5.1", 56 | "npm-run-all": "^4.1.5", 57 | "release-it": "^18.1.2", 58 | "sass": "^1.87.0", 59 | "simple-git-hooks": "^2.12.1", 60 | "tsx": "^4.19.3", 61 | "typescript": "~5.6.3", 62 | "unocss": "66.1.0-beta.7", 63 | "vite": "^6.3.2" 64 | }, 65 | "pnpm": { 66 | "patchedDependencies": { 67 | "pixi-live2d-display": "patches/pixi-live2d-display.patch" 68 | } 69 | }, 70 | "simple-git-hooks": { 71 | "commit-msg": "npx --no-install commitlint -e", 72 | "pre-commit": "npx lint-staged" 73 | }, 74 | "lint-staged": { 75 | "*": "eslint --fix" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /patches/pixi-live2d-display.patch: -------------------------------------------------------------------------------- 1 | diff --git a/core/README.md b/core/README.md 2 | deleted file mode 100644 3 | index ad383747237ee1a22ce39d01fbc7e77ac94b8e47..0000000000000000000000000000000000000000 4 | diff --git a/core/live2d.d.ts b/core/live2d.d.ts 5 | deleted file mode 100644 6 | index 0283512ed1c9ea01d7dd1b67b76d660237b453e8..0000000000000000000000000000000000000000 7 | diff --git a/cubism/.vscode/extensions.json b/cubism/.vscode/extensions.json 8 | deleted file mode 100644 9 | index fda5ad57b9567b939382ba15fb1d3b9f1fecf77e..0000000000000000000000000000000000000000 10 | diff --git a/cubism/.vscode/tasks.json b/cubism/.vscode/tasks.json 11 | deleted file mode 100644 12 | index 7cd3fffed85da69d5af154f63480bce8766a038f..0000000000000000000000000000000000000000 13 | diff --git a/types/index.d.ts b/types/index.d.ts 14 | index dff08ce9cdd9adefd15841a750c556fc3203c750..51bae2d4d8d5cd25c3b9219d96e91dc4c96bb95c 100644 15 | --- a/types/index.d.ts 16 | +++ b/types/index.d.ts 17 | @@ -1154,7 +1154,7 @@ export declare abstract class InternalModel extends EventEmitter { 18 | /** 19 | * The managed Live2D core model. 20 | */ 21 | - abstract readonly coreModel: object; 22 | + abstract readonly coreModel: CubismModel; 23 | abstract readonly settings: ModelSettings; 24 | focusController: FocusController; 25 | abstract motionManager: MotionManager; 26 | -------------------------------------------------------------------------------- /public/images/backgrounds/keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/images/backgrounds/keyboard.png -------------------------------------------------------------------------------- /public/images/backgrounds/standard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/images/backgrounds/standard.png -------------------------------------------------------------------------------- /public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/images/logo.png -------------------------------------------------------------------------------- /public/models/keyboard/cat.model3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 3, 3 | "FileReferences": { 4 | "Moc": "demomodel2.moc3", 5 | "Textures": [ 6 | "demomodel2.1024/texture_00.png", 7 | "demomodel2.1024/texture_01.png", 8 | "demomodel2.1024/texture_02.png" 9 | ], 10 | "DisplayInfo": "demomodel2.cdi3.json", 11 | "Expressions": [ 12 | { 13 | "Name": "live2d_expression0.exp3.json", 14 | "File": "live2d_expression0.exp3.json" 15 | }, 16 | { 17 | "Name": "live2d_expression1.exp3.json", 18 | "File": "live2d_expression1.exp3.json" 19 | }, 20 | { 21 | "Name": "live2d_expression2.exp3.json", 22 | "File": "live2d_expression2.exp3.json" 23 | } 24 | ], 25 | "Motions": { 26 | "CAT_motion": [ 27 | { 28 | "File": "live2d_motion1.motion3.json", 29 | "Sound": "live2d_motion1.flac", 30 | "FadeInTime": 0, 31 | "FadeOutTime": 0 32 | }, 33 | { 34 | "File": "live2d_motion2.motion3.json", 35 | "FadeInTime": 0, 36 | "FadeOutTime": 0 37 | } 38 | ], 39 | "CAT_motion_lock": [ 40 | { 41 | "File": "live2d_motion1.motion3.json", 42 | "Sound": "live2d_motion1.flac", 43 | "FadeInTime": 0, 44 | "FadeOutTime": 0 45 | }, 46 | { 47 | "File": "live2d_motion2.motion3.json", 48 | "FadeInTime": 0, 49 | "FadeOutTime": 0 50 | } 51 | ] 52 | } 53 | }, 54 | "Groups": [ 55 | { 56 | "Target": "Parameter", 57 | "Name": "EyeBlink", 58 | "Ids": [ 59 | "ParamEyeLOpen", 60 | "ParamEyeROpen" 61 | ] 62 | }, 63 | { 64 | "Target": "Parameter", 65 | "Name": "LipSync", 66 | "Ids": [] 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /public/models/keyboard/demomodel2.1024/texture_00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/models/keyboard/demomodel2.1024/texture_00.png -------------------------------------------------------------------------------- /public/models/keyboard/demomodel2.1024/texture_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/models/keyboard/demomodel2.1024/texture_01.png -------------------------------------------------------------------------------- /public/models/keyboard/demomodel2.1024/texture_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/models/keyboard/demomodel2.1024/texture_02.png -------------------------------------------------------------------------------- /public/models/keyboard/demomodel2.cdi3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 3, 3 | "Parameters": [ 4 | { 5 | "Id": "ParamAngleX", 6 | "GroupId": "", 7 | "Name": "角度 X" 8 | }, 9 | { 10 | "Id": "ParamAngleY", 11 | "GroupId": "", 12 | "Name": "角度 Y" 13 | }, 14 | { 15 | "Id": "CatParamRightHandDown", 16 | "GroupId": "", 17 | "Name": "右手按下" 18 | }, 19 | { 20 | "Id": "CatParamLeftHandDown", 21 | "GroupId": "", 22 | "Name": "左手按下" 23 | }, 24 | { 25 | "Id": "ParamAngleZ", 26 | "GroupId": "", 27 | "Name": "角度 Z" 28 | }, 29 | { 30 | "Id": "ParamEyeLOpen", 31 | "GroupId": "", 32 | "Name": "左眼 开闭" 33 | }, 34 | { 35 | "Id": "ParamEyeLSmile", 36 | "GroupId": "", 37 | "Name": "左眼 微笑" 38 | }, 39 | { 40 | "Id": "ParamEyeROpen", 41 | "GroupId": "", 42 | "Name": "右眼" 43 | }, 44 | { 45 | "Id": "ParamEyeRSmile", 46 | "GroupId": "", 47 | "Name": "右眼 微笑" 48 | }, 49 | { 50 | "Id": "Param3", 51 | "GroupId": "", 52 | "Name": "挥手" 53 | }, 54 | { 55 | "Id": "Param", 56 | "GroupId": "ParamGroup", 57 | "Name": "开启闪电" 58 | }, 59 | { 60 | "Id": "Param2", 61 | "GroupId": "ParamGroup", 62 | "Name": "闪电划过" 63 | }, 64 | { 65 | "Id": "Param4", 66 | "GroupId": "ParamGroup2", 67 | "Name": "表情:thuglife" 68 | }, 69 | { 70 | "Id": "Param5", 71 | "GroupId": "ParamGroup2", 72 | "Name": "表情:升天" 73 | }, 74 | { 75 | "Id": "ParamEyeBallX", 76 | "GroupId": "", 77 | "Name": "眼球 X" 78 | }, 79 | { 80 | "Id": "ParamEyeBallY", 81 | "GroupId": "", 82 | "Name": "眼球 Y" 83 | }, 84 | { 85 | "Id": "ParamBrowLY", 86 | "GroupId": "", 87 | "Name": "左眉上下" 88 | }, 89 | { 90 | "Id": "ParamBrowRY", 91 | "GroupId": "", 92 | "Name": "右眉 上下" 93 | }, 94 | { 95 | "Id": "ParamBrowLX", 96 | "GroupId": "", 97 | "Name": "左眉 左右" 98 | }, 99 | { 100 | "Id": "ParamBrowRX", 101 | "GroupId": "", 102 | "Name": "右眉 左右" 103 | }, 104 | { 105 | "Id": "ParamBrowLAngle", 106 | "GroupId": "", 107 | "Name": "左眉 角度" 108 | }, 109 | { 110 | "Id": "ParamBrowRAngle", 111 | "GroupId": "", 112 | "Name": "右眉 角度" 113 | }, 114 | { 115 | "Id": "ParamBrowLForm", 116 | "GroupId": "", 117 | "Name": "左眉 変形" 118 | }, 119 | { 120 | "Id": "ParamBrowRForm", 121 | "GroupId": "", 122 | "Name": "右眉 変形" 123 | }, 124 | { 125 | "Id": "ParamMouthForm", 126 | "GroupId": "", 127 | "Name": "嘴部 变形" 128 | }, 129 | { 130 | "Id": "ParamMouthOpenY", 131 | "GroupId": "", 132 | "Name": "嘴巴 张开和闭合" 133 | }, 134 | { 135 | "Id": "ParamCheek", 136 | "GroupId": "", 137 | "Name": "脸颊" 138 | }, 139 | { 140 | "Id": "ParamBodyAngleX", 141 | "GroupId": "", 142 | "Name": "身体旋转 X" 143 | }, 144 | { 145 | "Id": "ParamBodyAngleY", 146 | "GroupId": "", 147 | "Name": "身体旋转 Y" 148 | }, 149 | { 150 | "Id": "ParamBodyAngleZ", 151 | "GroupId": "", 152 | "Name": "身体旋转 Z" 153 | }, 154 | { 155 | "Id": "ParamBreath", 156 | "GroupId": "", 157 | "Name": "呼吸" 158 | }, 159 | { 160 | "Id": "ParamHairFront", 161 | "GroupId": "", 162 | "Name": "摇动 前发" 163 | }, 164 | { 165 | "Id": "ParamHairSide", 166 | "GroupId": "", 167 | "Name": "摇动 侧发" 168 | }, 169 | { 170 | "Id": "ParamHairBack", 171 | "GroupId": "", 172 | "Name": "摇动 后发" 173 | } 174 | ], 175 | "ParameterGroups": [ 176 | { 177 | "Id": "ParamGroup", 178 | "GroupId": "", 179 | "Name": "闪电" 180 | }, 181 | { 182 | "Id": "ParamGroup2", 183 | "GroupId": "", 184 | "Name": "表情" 185 | } 186 | ], 187 | "Parts": [ 188 | { 189 | "Id": "Part11", 190 | "Name": "demomodel.psd(未找到对应图层)" 191 | }, 192 | { 193 | "Id": "Part7", 194 | "Name": "demomodel.psd(未找到对应图层)" 195 | }, 196 | { 197 | "Id": "Part3", 198 | "Name": "demomodel.psd(未找到对应图层)" 199 | }, 200 | { 201 | "Id": "Part2", 202 | "Name": "demomodel.psd(未找到对应图层)" 203 | }, 204 | { 205 | "Id": "Part", 206 | "Name": "demomodel.psd(未找到对应图层)" 207 | }, 208 | { 209 | "Id": "Part10", 210 | "Name": "天使环" 211 | }, 212 | { 213 | "Id": "Part5", 214 | "Name": "demomodel.psd(未找到对应图层)" 215 | }, 216 | { 217 | "Id": "PartSketch0", 218 | "Name": "[ 参考图 ]" 219 | }, 220 | { 221 | "Id": "Part8", 222 | "Name": "thug life" 223 | }, 224 | { 225 | "Id": "Part6", 226 | "Name": "闪电" 227 | }, 228 | { 229 | "Id": "Part4", 230 | "Name": "闪电" 231 | } 232 | ] 233 | } 234 | -------------------------------------------------------------------------------- /public/models/keyboard/demomodel2.moc3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/models/keyboard/demomodel2.moc3 -------------------------------------------------------------------------------- /public/models/keyboard/exp_1.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "Parameters": [ 4 | { 5 | "Id": "ParamEyeLOpen", 6 | "Value": 0.321, 7 | "Blend": "Multiply" 8 | }, 9 | { 10 | "Id": "ParamEyeROpen", 11 | "Value": 0.313, 12 | "Blend": "Multiply" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /public/models/keyboard/exp_2.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "Parameters": [ 4 | { 5 | "Id": "ParamEyeLOpen", 6 | "Value": -1, 7 | "Blend": "Add" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /public/models/keyboard/live2d_expression0.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "Parameters": [] 4 | } 5 | -------------------------------------------------------------------------------- /public/models/keyboard/live2d_expression1.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "FadeInTime": 0.8, 4 | "Parameters": [ 5 | { 6 | "Id": "Param4", 7 | "Value": 1, 8 | "Blend": "Add" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /public/models/keyboard/live2d_expression2.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "FadeInTime": 0.5, 4 | "Parameters": [ 5 | { 6 | "Id": "Param5", 7 | "Value": 1, 8 | "Blend": "Add" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /public/models/keyboard/live2d_motion1.flac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/models/keyboard/live2d_motion1.flac -------------------------------------------------------------------------------- /public/models/keyboard/live2d_motion1.motion3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 3, 3 | "Meta": { 4 | "Duration": 1.633, 5 | "Fps": 30.0, 6 | "Loop": true, 7 | "AreBeziersRestricted": false, 8 | "CurveCount": 2, 9 | "TotalSegmentCount": 8, 10 | "TotalPointCount": 20, 11 | "UserDataCount": 0, 12 | "TotalUserDataSize": 0 13 | }, 14 | "Curves": [ 15 | { 16 | "Target": "Parameter", 17 | "Id": "Param", 18 | "Segments": [ 19 | 0, 20 | 0, 21 | 1, 22 | 0.033, 23 | 0, 24 | 0.067, 25 | 1, 26 | 0.1, 27 | 1, 28 | 1, 29 | 0.411, 30 | 1, 31 | 0.722, 32 | 1, 33 | 1.033, 34 | 1, 35 | 1, 36 | 1.189, 37 | 1, 38 | 1.344, 39 | 0, 40 | 1.5, 41 | 0, 42 | 0, 43 | 1.633, 44 | 0 45 | ] 46 | }, 47 | { 48 | "Target": "Parameter", 49 | "Id": "Param2", 50 | "Segments": [ 51 | 0, 52 | 0, 53 | 0, 54 | 0.067, 55 | 0, 56 | 1, 57 | 0.1, 58 | 0, 59 | 0.133, 60 | 0.142, 61 | 0.167, 62 | 0.2, 63 | 1, 64 | 0.489, 65 | 0.764, 66 | 0.811, 67 | 1, 68 | 1.133, 69 | 1, 70 | 0, 71 | 1.633, 72 | 1 73 | ] 74 | } 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /public/models/keyboard/live2d_motion2.motion3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 3, 3 | "Meta": { 4 | "Duration": 2.333, 5 | "Fps": 30.0, 6 | "Loop": true, 7 | "AreBeziersRestricted": true, 8 | "CurveCount": 2, 9 | "TotalSegmentCount": 7, 10 | "TotalPointCount": 21, 11 | "UserDataCount": 0, 12 | "TotalUserDataSize": 0 13 | }, 14 | "Curves": [ 15 | { 16 | "Target": "Parameter", 17 | "Id": "CatParamLeftHandDown", 18 | "Segments": [ 19 | 0, 20 | 0, 21 | 0, 22 | 2.333, 23 | 0 24 | ] 25 | }, 26 | { 27 | "Target": "Parameter", 28 | "Id": "Param3", 29 | "Segments": [ 30 | 0, 31 | 0, 32 | 1, 33 | 0.133, 34 | 0, 35 | 0.267, 36 | 30, 37 | 0.4, 38 | 30, 39 | 1, 40 | 0.522, 41 | 30, 42 | 0.644, 43 | 0, 44 | 0.767, 45 | 0, 46 | 1, 47 | 0.9, 48 | 0, 49 | 1.033, 50 | 30, 51 | 1.167, 52 | 30, 53 | 1, 54 | 1.3, 55 | 30, 56 | 1.433, 57 | 0, 58 | 1.567, 59 | 0, 60 | 1, 61 | 1.7, 62 | 0, 63 | 1.833, 64 | 30, 65 | 1.967, 66 | 30, 67 | 1, 68 | 2.089, 69 | 30, 70 | 2.211, 71 | 0, 72 | 2.333, 73 | 0 74 | ] 75 | } 76 | ] 77 | } 78 | -------------------------------------------------------------------------------- /public/models/standard/cat.model3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 3, 3 | "FileReferences": { 4 | "Moc": "demomodel.moc3", 5 | "Textures": [ 6 | "demomodel.1024/texture_00.png", 7 | "demomodel.1024/texture_01.png", 8 | "demomodel.1024/texture_02.png" 9 | ], 10 | "DisplayInfo": "demomodel.cdi3.json", 11 | "Expressions": [ 12 | { 13 | "Key": "", 14 | "Name": "去掉表情", 15 | "File": "live2d_expression0.exp3.json" 16 | }, 17 | { 18 | "Key": "", 19 | "Name": "戴上墨镜", 20 | "File": "live2d_expression1.exp3.json" 21 | }, 22 | { 23 | "Key": "", 24 | "Name": "升天", 25 | "File": "live2d_expression2.exp3.json" 26 | } 27 | ], 28 | "Motions": { 29 | "CAT_motion": [ 30 | { 31 | "Key": "", 32 | "Name": "打雷", 33 | "File": "live2d_motion1.motion3.json", 34 | "Sound": "live2d_motion1.flac", 35 | "FadeInTime": 0, 36 | "FadeOutTime": 0 37 | }, 38 | { 39 | "Key": "", 40 | "Name": "左手摇摆", 41 | "File": "live2d_motion2.motion3.json", 42 | "FadeInTime": 0, 43 | "FadeOutTime": 0 44 | } 45 | ] 46 | } 47 | }, 48 | "Groups": [ 49 | { 50 | "Target": "Parameter", 51 | "Name": "EyeBlink", 52 | "Ids": [ 53 | "ParamEyeLOpen", 54 | "ParamEyeROpen" 55 | ] 56 | }, 57 | { 58 | "Target": "Parameter", 59 | "Name": "LipSync", 60 | "Ids": [] 61 | } 62 | ], 63 | "HitAreas": [] 64 | } 65 | -------------------------------------------------------------------------------- /public/models/standard/demomodel.1024/texture_00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/models/standard/demomodel.1024/texture_00.png -------------------------------------------------------------------------------- /public/models/standard/demomodel.1024/texture_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/models/standard/demomodel.1024/texture_01.png -------------------------------------------------------------------------------- /public/models/standard/demomodel.1024/texture_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/models/standard/demomodel.1024/texture_02.png -------------------------------------------------------------------------------- /public/models/standard/demomodel.cdi3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 3, 3 | "Parameters": [ 4 | { 5 | "Id": "ParamAngleX", 6 | "GroupId": "", 7 | "Name": "角度 X" 8 | }, 9 | { 10 | "Id": "ParamAngleY", 11 | "GroupId": "", 12 | "Name": "角度 Y" 13 | }, 14 | { 15 | "Id": "ParamMouseX", 16 | "GroupId": "", 17 | "Name": "鼠标X" 18 | }, 19 | { 20 | "Id": "ParamMouseY", 21 | "GroupId": "", 22 | "Name": "鼠标Y" 23 | }, 24 | { 25 | "Id": "ParamMouseLeftDown", 26 | "GroupId": "", 27 | "Name": "鼠标左键按下" 28 | }, 29 | { 30 | "Id": "ParamMouseRightDown", 31 | "GroupId": "", 32 | "Name": "鼠标右键按下" 33 | }, 34 | { 35 | "Id": "CatParamLeftHandDown", 36 | "GroupId": "", 37 | "Name": "键盘按下" 38 | }, 39 | { 40 | "Id": "ParamAngleZ", 41 | "GroupId": "", 42 | "Name": "角度 Z" 43 | }, 44 | { 45 | "Id": "ParamEyeLOpen", 46 | "GroupId": "", 47 | "Name": "左眼 开闭" 48 | }, 49 | { 50 | "Id": "ParamEyeLSmile", 51 | "GroupId": "", 52 | "Name": "左眼 微笑" 53 | }, 54 | { 55 | "Id": "ParamEyeROpen", 56 | "GroupId": "", 57 | "Name": "右眼" 58 | }, 59 | { 60 | "Id": "ParamEyeRSmile", 61 | "GroupId": "", 62 | "Name": "右眼 微笑" 63 | }, 64 | { 65 | "Id": "Param3", 66 | "GroupId": "", 67 | "Name": "挥手" 68 | }, 69 | { 70 | "Id": "Param", 71 | "GroupId": "ParamGroup", 72 | "Name": "开启闪电" 73 | }, 74 | { 75 | "Id": "Param2", 76 | "GroupId": "ParamGroup", 77 | "Name": "闪电划过" 78 | }, 79 | { 80 | "Id": "Param4", 81 | "GroupId": "ParamGroup2", 82 | "Name": "表情:thuglife" 83 | }, 84 | { 85 | "Id": "Param5", 86 | "GroupId": "ParamGroup2", 87 | "Name": "表情:升天" 88 | }, 89 | { 90 | "Id": "ParamEyeBallX", 91 | "GroupId": "", 92 | "Name": "眼球 X" 93 | }, 94 | { 95 | "Id": "ParamEyeBallY", 96 | "GroupId": "", 97 | "Name": "眼球 Y" 98 | }, 99 | { 100 | "Id": "ParamBrowLY", 101 | "GroupId": "", 102 | "Name": "左眉上下" 103 | }, 104 | { 105 | "Id": "ParamBrowRY", 106 | "GroupId": "", 107 | "Name": "右眉 上下" 108 | }, 109 | { 110 | "Id": "ParamBrowLX", 111 | "GroupId": "", 112 | "Name": "左眉 左右" 113 | }, 114 | { 115 | "Id": "ParamBrowRX", 116 | "GroupId": "", 117 | "Name": "右眉 左右" 118 | }, 119 | { 120 | "Id": "ParamBrowLAngle", 121 | "GroupId": "", 122 | "Name": "左眉 角度" 123 | }, 124 | { 125 | "Id": "ParamBrowRAngle", 126 | "GroupId": "", 127 | "Name": "右眉 角度" 128 | }, 129 | { 130 | "Id": "ParamBrowLForm", 131 | "GroupId": "", 132 | "Name": "左眉 変形" 133 | }, 134 | { 135 | "Id": "ParamBrowRForm", 136 | "GroupId": "", 137 | "Name": "右眉 変形" 138 | }, 139 | { 140 | "Id": "ParamMouthForm", 141 | "GroupId": "", 142 | "Name": "嘴部 变形" 143 | }, 144 | { 145 | "Id": "ParamMouthOpenY", 146 | "GroupId": "", 147 | "Name": "嘴巴 张开和闭合" 148 | }, 149 | { 150 | "Id": "ParamCheek", 151 | "GroupId": "", 152 | "Name": "脸颊" 153 | }, 154 | { 155 | "Id": "ParamBodyAngleX", 156 | "GroupId": "", 157 | "Name": "身体旋转 X" 158 | }, 159 | { 160 | "Id": "ParamBodyAngleY", 161 | "GroupId": "", 162 | "Name": "身体旋转 Y" 163 | }, 164 | { 165 | "Id": "ParamBodyAngleZ", 166 | "GroupId": "", 167 | "Name": "身体旋转 Z" 168 | }, 169 | { 170 | "Id": "ParamBreath", 171 | "GroupId": "", 172 | "Name": "呼吸" 173 | }, 174 | { 175 | "Id": "ParamHairFront", 176 | "GroupId": "", 177 | "Name": "摇动 前发" 178 | }, 179 | { 180 | "Id": "ParamHairSide", 181 | "GroupId": "", 182 | "Name": "摇动 侧发" 183 | }, 184 | { 185 | "Id": "ParamHairBack", 186 | "GroupId": "", 187 | "Name": "摇动 后发" 188 | } 189 | ], 190 | "ParameterGroups": [ 191 | { 192 | "Id": "ParamGroup", 193 | "GroupId": "", 194 | "Name": "闪电" 195 | }, 196 | { 197 | "Id": "ParamGroup2", 198 | "GroupId": "", 199 | "Name": "表情" 200 | } 201 | ], 202 | "Parts": [ 203 | { 204 | "Id": "Part11", 205 | "Name": "demomodel.psd(未找到对应图层)" 206 | }, 207 | { 208 | "Id": "Part7", 209 | "Name": "demomodel.psd(未找到对应图层)" 210 | }, 211 | { 212 | "Id": "Part3", 213 | "Name": "demomodel.psd(未找到对应图层)" 214 | }, 215 | { 216 | "Id": "Part2", 217 | "Name": "demomodel.psd(未找到对应图层)" 218 | }, 219 | { 220 | "Id": "Part", 221 | "Name": "demomodel.psd(未找到对应图层)" 222 | }, 223 | { 224 | "Id": "Part10", 225 | "Name": "天使环" 226 | }, 227 | { 228 | "Id": "Part5", 229 | "Name": "demomodel.psd(未找到对应图层)" 230 | }, 231 | { 232 | "Id": "Part8", 233 | "Name": "thug life" 234 | }, 235 | { 236 | "Id": "Part6", 237 | "Name": "闪电" 238 | }, 239 | { 240 | "Id": "Part4", 241 | "Name": "闪电" 242 | } 243 | ] 244 | } 245 | -------------------------------------------------------------------------------- /public/models/standard/demomodel.moc3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/models/standard/demomodel.moc3 -------------------------------------------------------------------------------- /public/models/standard/exp_1.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "Parameters": [ 4 | { 5 | "Id": "ParamEyeLOpen", 6 | "Value": 0.321, 7 | "Blend": "Multiply" 8 | }, 9 | { 10 | "Id": "ParamEyeROpen", 11 | "Value": 0.313, 12 | "Blend": "Multiply" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /public/models/standard/exp_2.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "Parameters": [ 4 | { 5 | "Id": "ParamEyeLOpen", 6 | "Value": -1, 7 | "Blend": "Add" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /public/models/standard/live2d_expression0.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "Parameters": [] 4 | } 5 | -------------------------------------------------------------------------------- /public/models/standard/live2d_expression1.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "FadeInTime": 0.8, 4 | "Parameters": [ 5 | { 6 | "Id": "Param4", 7 | "Value": 1, 8 | "Blend": "Add" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /public/models/standard/live2d_expression2.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "FadeInTime": 0.5, 4 | "Parameters": [ 5 | { 6 | "Id": "Param5", 7 | "Value": 1, 8 | "Blend": "Add" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /public/models/standard/live2d_motion1.flac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/public/models/standard/live2d_motion1.flac -------------------------------------------------------------------------------- /public/models/standard/live2d_motion1.motion3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 3, 3 | "Meta": { 4 | "Duration": 1.633, 5 | "Fps": 30.0, 6 | "Loop": true, 7 | "AreBeziersRestricted": false, 8 | "CurveCount": 2, 9 | "TotalSegmentCount": 8, 10 | "TotalPointCount": 20, 11 | "UserDataCount": 0, 12 | "TotalUserDataSize": 0 13 | }, 14 | "Curves": [ 15 | { 16 | "Target": "Parameter", 17 | "Id": "Param", 18 | "Segments": [ 19 | 0, 20 | 0, 21 | 1, 22 | 0.033, 23 | 0, 24 | 0.067, 25 | 1, 26 | 0.1, 27 | 1, 28 | 1, 29 | 0.411, 30 | 1, 31 | 0.722, 32 | 1, 33 | 1.033, 34 | 1, 35 | 1, 36 | 1.189, 37 | 1, 38 | 1.344, 39 | 0, 40 | 1.5, 41 | 0, 42 | 0, 43 | 1.633, 44 | 0 45 | ] 46 | }, 47 | { 48 | "Target": "Parameter", 49 | "Id": "Param2", 50 | "Segments": [ 51 | 0, 52 | 0, 53 | 0, 54 | 0.067, 55 | 0, 56 | 1, 57 | 0.1, 58 | 0, 59 | 0.133, 60 | 0.142, 61 | 0.167, 62 | 0.2, 63 | 1, 64 | 0.489, 65 | 0.764, 66 | 0.811, 67 | 1, 68 | 1.133, 69 | 1, 70 | 0, 71 | 1.633, 72 | 1 73 | ] 74 | } 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /public/models/standard/live2d_motion2.motion3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 3, 3 | "Meta": { 4 | "Duration": 2.333, 5 | "Fps": 30.0, 6 | "Loop": true, 7 | "AreBeziersRestricted": true, 8 | "CurveCount": 2, 9 | "TotalSegmentCount": 7, 10 | "TotalPointCount": 21, 11 | "UserDataCount": 0, 12 | "TotalUserDataSize": 0 13 | }, 14 | "Curves": [ 15 | { 16 | "Target": "Parameter", 17 | "Id": "CatParamLeftHandDown", 18 | "Segments": [ 19 | 0, 20 | 0, 21 | 0, 22 | 2.333, 23 | 0 24 | ] 25 | }, 26 | { 27 | "Target": "Parameter", 28 | "Id": "Param3", 29 | "Segments": [ 30 | 0, 31 | 0, 32 | 1, 33 | 0.133, 34 | 0, 35 | 0.267, 36 | 30, 37 | 0.4, 38 | 30, 39 | 1, 40 | 0.522, 41 | 30, 42 | 0.644, 43 | 0, 44 | 0.767, 45 | 0, 46 | 1, 47 | 0.9, 48 | 0, 49 | 1.033, 50 | 30, 51 | 1.167, 52 | 30, 53 | 1, 54 | 1.3, 55 | 30, 56 | 1.433, 57 | 0, 58 | 1.567, 59 | 0, 60 | 1, 61 | 1.7, 62 | 0, 63 | 1.833, 64 | 30, 65 | 1.967, 66 | 30, 67 | 1, 68 | 2.089, 69 | 30, 70 | 2.211, 71 | 0, 72 | 2.333, 73 | 0 74 | ] 75 | } 76 | ] 77 | } 78 | -------------------------------------------------------------------------------- /scripts/buildIcon.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from 'node:child_process' 2 | import { env, platform } from 'node:process' 3 | 4 | (() => { 5 | const isMac = env.PLATFORM?.startsWith('macos') ?? platform === 'darwin' 6 | 7 | const logoName = isMac ? 'logo-mac' : 'logo' 8 | 9 | const command = `tauri icon src-tauri/assets/${logoName}.png` 10 | 11 | execSync(command, { stdio: 'inherit' }) 12 | })() 13 | -------------------------------------------------------------------------------- /scripts/release.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from 'node:fs' 2 | import { dirname, resolve } from 'node:path' 3 | import { fileURLToPath } from 'node:url' 4 | 5 | import { name, version } from '../package.json' 6 | 7 | const __dirname = dirname(fileURLToPath(import.meta.url)); 8 | 9 | (() => { 10 | const tomlPath = resolve(__dirname, '..', 'src-tauri', 'Cargo.toml') 11 | const lockPath = resolve(__dirname, '..', 'Cargo.lock') 12 | 13 | for (const path of [tomlPath, lockPath]) { 14 | let content = readFileSync(path, 'utf-8') 15 | 16 | const regexp = new RegExp( 17 | `(name\\s*=\\s*"${name}"\\s*version\\s*=\\s*)"(\\d+\\.\\d+\\.\\d+(-\\w+\\.\\d+)?)"`, 18 | ) 19 | 20 | content = content.replace(regexp, `$1"${version}"`) 21 | 22 | writeFileSync(path, content) 23 | } 24 | })() 25 | -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | 9 | icons 10 | autogenerated 11 | schemas -------------------------------------------------------------------------------- /src-tauri/BongoCat.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name={{{name}}} 4 | Exec={{{exec}}} 5 | Icon={{{icon}}} 6 | Categories={{{categories}}} 7 | Comment={{{comment}}} 8 | Terminal=false 9 | -------------------------------------------------------------------------------- /src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bongo-cat" 3 | version = "0.2.1" 4 | description = "A Tauri App" 5 | authors = [ "ayangweb" ] 6 | edition = "2021" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [lib] 11 | # The `_lib` suffix may seem redundant but it is necessary 12 | # to make the lib name unique and wouldn't conflict with the bin name. 13 | # This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519 14 | name = "bongo_cat_lib" 15 | crate-type = ["staticlib", "cdylib", "rlib"] 16 | 17 | [build-dependencies] 18 | tauri-build = { version = "2", features = [] } 19 | 20 | [dependencies] 21 | tauri = { workspace = true, features = ["tray-icon", "protocol-asset", "macos-private-api", "image-png"] } 22 | serde = { workspace = true, features = ["derive"] } 23 | serde_json.workspace = true 24 | tauri-plugin-custom-window.workspace = true 25 | rdev = "0.5" 26 | tauri-plugin-os = "2" 27 | tauri-plugin-process = "2" 28 | tauri-plugin-opener = "2" 29 | tauri-plugin-pinia = "3" 30 | tauri-plugin-log = "2" 31 | tauri-plugin-updater = "2" 32 | tauri-plugin-prevent-default = "1" 33 | tauri-plugin-single-instance = "2" 34 | 35 | [target."cfg(target_os = \"macos\")".dependencies] 36 | tauri-nspanel.workspace = true 37 | 38 | [features] 39 | cargo-clippy = [] 40 | -------------------------------------------------------------------------------- /src-tauri/assets/logo-mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src-tauri/assets/logo-mac.png -------------------------------------------------------------------------------- /src-tauri/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src-tauri/assets/logo.png -------------------------------------------------------------------------------- /src-tauri/assets/tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src-tauri/assets/tray.png -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /src-tauri/capabilities/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../gen/schemas/desktop-schema.json", 3 | "identifier": "default", 4 | "description": "Capability for the main window", 5 | "windows": [ 6 | "*" 7 | ], 8 | "permissions": [ 9 | "core:default", 10 | "core:window:allow-start-dragging", 11 | "core:window:allow-set-size", 12 | "core:window:deny-internal-toggle-maximize", 13 | "core:window:allow-set-always-on-top", 14 | "core:window:allow-set-ignore-cursor-events", 15 | "core:window:allow-set-decorations", 16 | "core:window:allow-set-position", 17 | "custom-window:default", 18 | "os:default", 19 | "process:default", 20 | "opener:default", 21 | "pinia:default", 22 | "log:default", 23 | "updater:default", 24 | "prevent-default:default" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src-tauri/src/core/device.rs: -------------------------------------------------------------------------------- 1 | use rdev::{listen, Event, EventType}; 2 | use serde::Serialize; 3 | use serde_json::{json, Value}; 4 | use std::sync::atomic::{AtomicBool, Ordering}; 5 | use tauri::{AppHandle, Emitter}; 6 | 7 | static IS_RUNNING: AtomicBool = AtomicBool::new(false); 8 | 9 | #[derive(Debug, Clone, Serialize)] 10 | pub enum DeviceKind { 11 | MousePress, 12 | MouseRelease, 13 | MouseMove, 14 | KeyboardPress, 15 | KeyboardRelease, 16 | } 17 | 18 | #[derive(Debug, Clone, Serialize)] 19 | pub struct DeviceEvent { 20 | kind: DeviceKind, 21 | value: Value, 22 | } 23 | 24 | pub fn start_listening(app_handle: AppHandle) { 25 | if IS_RUNNING.load(Ordering::SeqCst) { 26 | return; 27 | } 28 | 29 | IS_RUNNING.store(true, Ordering::SeqCst); 30 | 31 | let callback = move |event: Event| { 32 | let device = match event.event_type { 33 | EventType::ButtonPress(button) => DeviceEvent { 34 | kind: DeviceKind::MousePress, 35 | value: json!(format!("{:?}", button)), 36 | }, 37 | EventType::ButtonRelease(button) => DeviceEvent { 38 | kind: DeviceKind::MouseRelease, 39 | value: json!(format!("{:?}", button)), 40 | }, 41 | EventType::MouseMove { x, y } => DeviceEvent { 42 | kind: DeviceKind::MouseMove, 43 | value: json!({ "x": x, "y": y }), 44 | }, 45 | EventType::KeyPress(key) => DeviceEvent { 46 | kind: DeviceKind::KeyboardPress, 47 | value: json!(format!("{:?}", key)), 48 | }, 49 | EventType::KeyRelease(key) => DeviceEvent { 50 | kind: DeviceKind::KeyboardRelease, 51 | value: json!(format!("{:?}", key)), 52 | }, 53 | _ => return, 54 | }; 55 | 56 | if let Err(e) = app_handle.emit("device-changed", device) { 57 | eprintln!("Failed to emit event: {:?}", e); 58 | } 59 | }; 60 | 61 | #[cfg(target_os = "macos")] 62 | if let Err(e) = listen(callback) { 63 | eprintln!("Device listening error: {:?}", e); 64 | } 65 | 66 | #[cfg(not(target_os = "macos"))] 67 | std::thread::spawn(move || { 68 | if let Err(e) = listen(callback) { 69 | eprintln!("Device listening error: {:?}", e); 70 | } 71 | }); 72 | } 73 | -------------------------------------------------------------------------------- /src-tauri/src/core/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod device; 2 | pub mod prevent_default; 3 | pub mod setup; 4 | -------------------------------------------------------------------------------- /src-tauri/src/core/prevent_default.rs: -------------------------------------------------------------------------------- 1 | pub fn init() -> tauri::plugin::TauriPlugin { 2 | #[cfg(debug_assertions)] 3 | { 4 | use tauri_plugin_prevent_default::Flags; 5 | 6 | tauri_plugin_prevent_default::Builder::new() 7 | .with_flags(Flags::all().difference(Flags::CONTEXT_MENU)) 8 | .build() 9 | } 10 | 11 | #[cfg(not(debug_assertions))] 12 | tauri_plugin_prevent_default::init() 13 | } 14 | -------------------------------------------------------------------------------- /src-tauri/src/core/setup/linux.rs: -------------------------------------------------------------------------------- 1 | use tauri::{AppHandle, WebviewWindow}; 2 | 3 | pub fn platform( 4 | _app_handle: &AppHandle, 5 | _main_window: WebviewWindow, 6 | _preference_window: WebviewWindow, 7 | ) { 8 | } 9 | -------------------------------------------------------------------------------- /src-tauri/src/core/setup/macos.rs: -------------------------------------------------------------------------------- 1 | use tauri::{ActivationPolicy, AppHandle, Emitter, EventTarget, WebviewWindow}; 2 | use tauri_nspanel::{ 3 | cocoa::appkit::{NSMainMenuWindowLevel, NSWindowCollectionBehavior}, 4 | panel_delegate, WebviewWindowExt, 5 | }; 6 | use tauri_plugin_custom_window::MAIN_WINDOW_LABEL; 7 | 8 | #[allow(non_upper_case_globals)] 9 | const NSWindowStyleMaskNonActivatingPanel: i32 = 1 << 7; 10 | #[allow(non_upper_case_globals)] 11 | const NSResizableWindowMask: i32 = 1 << 3; 12 | const WINDOW_FOCUS_EVENT: &str = "tauri://focus"; 13 | const WINDOW_BLUR_EVENT: &str = "tauri://blur"; 14 | const WINDOW_MOVED_EVENT: &str = "tauri://move"; 15 | const WINDOW_RESIZED_EVENT: &str = "tauri://resize"; 16 | 17 | pub fn platform( 18 | app_handle: &AppHandle, 19 | main_window: WebviewWindow, 20 | _preference_window: WebviewWindow, 21 | ) { 22 | let _ = app_handle.plugin(tauri_nspanel::init()); 23 | 24 | let _ = app_handle.set_activation_policy(ActivationPolicy::Accessory); 25 | 26 | let panel = main_window.to_panel().unwrap(); 27 | 28 | panel.set_level(NSMainMenuWindowLevel); 29 | 30 | panel.set_style_mask(NSWindowStyleMaskNonActivatingPanel | NSResizableWindowMask); 31 | 32 | panel.set_collection_behaviour( 33 | NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces 34 | | NSWindowCollectionBehavior::NSWindowCollectionBehaviorStationary 35 | | NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary, 36 | ); 37 | 38 | let delegate = panel_delegate!(EcoPanelDelegate { 39 | window_did_become_key, 40 | window_did_resign_key, 41 | window_did_resize, 42 | window_did_move 43 | }); 44 | 45 | delegate.set_listener(Box::new(move |delegate_name: String| { 46 | let target = EventTarget::labeled(MAIN_WINDOW_LABEL); 47 | 48 | let window_move_event = || { 49 | if let Ok(position) = main_window.outer_position() { 50 | let _ = main_window.emit_to(target.clone(), WINDOW_MOVED_EVENT, position); 51 | } 52 | }; 53 | 54 | match delegate_name.as_str() { 55 | "window_did_become_key" => { 56 | let _ = main_window.emit_to(target, WINDOW_FOCUS_EVENT, true); 57 | } 58 | "window_did_resign_key" => { 59 | let _ = main_window.emit_to(target, WINDOW_BLUR_EVENT, true); 60 | } 61 | "window_did_resize" => { 62 | window_move_event(); 63 | 64 | if let Ok(size) = main_window.inner_size() { 65 | let _ = main_window.emit_to(target, WINDOW_RESIZED_EVENT, size); 66 | } 67 | } 68 | "window_did_move" => window_move_event(), 69 | _ => (), 70 | } 71 | })); 72 | 73 | panel.set_delegate(delegate); 74 | } 75 | -------------------------------------------------------------------------------- /src-tauri/src/core/setup/mod.rs: -------------------------------------------------------------------------------- 1 | use tauri::{AppHandle, WebviewWindow}; 2 | 3 | #[cfg(target_os = "macos")] 4 | mod macos; 5 | 6 | #[cfg(target_os = "windows")] 7 | mod windows; 8 | 9 | #[cfg(target_os = "linux")] 10 | mod linux; 11 | 12 | #[cfg(target_os = "macos")] 13 | pub use macos::*; 14 | 15 | #[cfg(target_os = "windows")] 16 | pub use windows::*; 17 | 18 | #[cfg(target_os = "linux")] 19 | pub use linux::*; 20 | 21 | pub fn default( 22 | app_handle: &AppHandle, 23 | main_window: WebviewWindow, 24 | preference_window: WebviewWindow, 25 | ) { 26 | #[cfg(any(dev, debug_assertions))] 27 | main_window.open_devtools(); 28 | 29 | platform(app_handle, main_window.clone(), preference_window.clone()); 30 | } 31 | -------------------------------------------------------------------------------- /src-tauri/src/core/setup/windows.rs: -------------------------------------------------------------------------------- 1 | use tauri::{AppHandle, WebviewWindow}; 2 | 3 | pub fn platform( 4 | _app_handle: &AppHandle, 5 | _main_window: WebviewWindow, 6 | _preference_window: WebviewWindow, 7 | ) { 8 | } 9 | -------------------------------------------------------------------------------- /src-tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod core; 2 | 3 | use core::{device, prevent_default, setup}; 4 | use tauri::{Manager, WindowEvent}; 5 | use tauri_plugin_custom_window::{ 6 | show_preference_window, MAIN_WINDOW_LABEL, PREFERENCE_WINDOW_LABEL, 7 | }; 8 | 9 | #[cfg_attr(mobile, tauri::mobile_entry_point)] 10 | pub fn run() { 11 | tauri::Builder::default() 12 | .setup(|app| { 13 | let app_handle = app.handle(); 14 | 15 | let main_window = app.get_webview_window(MAIN_WINDOW_LABEL).unwrap(); 16 | 17 | let preference_window = app.get_webview_window(PREFERENCE_WINDOW_LABEL).unwrap(); 18 | 19 | setup::default(&app_handle, main_window.clone(), preference_window.clone()); 20 | 21 | device::start_listening(app_handle.clone()); 22 | 23 | Ok(()) 24 | }) 25 | .plugin(tauri_plugin_custom_window::init()) 26 | .plugin(tauri_plugin_os::init()) 27 | .plugin(tauri_plugin_process::init()) 28 | .plugin(tauri_plugin_opener::init()) 29 | .plugin(tauri_plugin_pinia::init()) 30 | .plugin(tauri_plugin_updater::Builder::new().build()) 31 | .plugin(prevent_default::init()) 32 | .plugin(tauri_plugin_single_instance::init( 33 | |app_handle, _argv, _cwd| { 34 | show_preference_window(app_handle); 35 | }, 36 | )) 37 | .on_window_event(|window, event| match event { 38 | WindowEvent::CloseRequested { api, .. } => { 39 | let _ = window.hide(); 40 | 41 | api.prevent_close(); 42 | } 43 | _ => {} 44 | }) 45 | .plugin(tauri_plugin_log::Builder::new().build()) 46 | .run(tauri::generate_context!()) 47 | .expect("error while running tauri application"); 48 | } 49 | -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 2 | 3 | fn main() { 4 | bongo_cat_lib::run() 5 | } 6 | -------------------------------------------------------------------------------- /src-tauri/src/plugins/window/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tauri-plugin-custom-window" 3 | version = "0.1.0" 4 | authors = [] 5 | description = "" 6 | edition = "2021" 7 | rust-version = "1.77.2" 8 | links = "tauri-plugin-custom-window" 9 | 10 | [dependencies] 11 | tauri.workspace = true 12 | serde.workspace = true 13 | 14 | [build-dependencies] 15 | tauri-plugin.workspace = true 16 | 17 | [target."cfg(target_os = \"macos\")".dependencies] 18 | tauri-nspanel.workspace = true 19 | -------------------------------------------------------------------------------- /src-tauri/src/plugins/window/build.rs: -------------------------------------------------------------------------------- 1 | const COMMANDS: &[&str] = &["show_window", "hide_window"]; 2 | 3 | fn main() { 4 | tauri_plugin::Builder::new(COMMANDS).build(); 5 | } 6 | -------------------------------------------------------------------------------- /src-tauri/src/plugins/window/permissions/default.toml: -------------------------------------------------------------------------------- 1 | "$schema" = "schemas/schema.json" 2 | 3 | [default] 4 | description = "Default permissions for the plugin" 5 | permissions = ["allow-show-window", "allow-hide-window"] 6 | -------------------------------------------------------------------------------- /src-tauri/src/plugins/window/src/commands/macos.rs: -------------------------------------------------------------------------------- 1 | use super::{is_main_window, shared_hide_window, shared_show_window}; 2 | use crate::MAIN_WINDOW_LABEL; 3 | use tauri::{command, AppHandle, Runtime, WebviewWindow}; 4 | use tauri_nspanel::ManagerExt; 5 | 6 | pub enum MacOSPanelStatus { 7 | Show, 8 | Hide, 9 | } 10 | 11 | #[command] 12 | pub async fn show_window(app_handle: AppHandle, window: WebviewWindow) { 13 | if is_main_window(&window) { 14 | set_macos_panel(&app_handle, &window, MacOSPanelStatus::Show); 15 | } else { 16 | shared_show_window(&app_handle, &window); 17 | } 18 | } 19 | 20 | #[command] 21 | pub async fn hide_window(app_handle: AppHandle, window: WebviewWindow) { 22 | if is_main_window(&window) { 23 | set_macos_panel(&app_handle, &window, MacOSPanelStatus::Hide); 24 | } else { 25 | shared_hide_window(&app_handle, &window); 26 | } 27 | } 28 | 29 | pub fn set_macos_panel( 30 | app_handle: &AppHandle, 31 | window: &WebviewWindow, 32 | status: MacOSPanelStatus, 33 | ) { 34 | if is_main_window(window) { 35 | let app_handle_clone = app_handle.clone(); 36 | 37 | let _ = app_handle.run_on_main_thread(move || { 38 | if let Ok(panel) = app_handle_clone.get_webview_panel(MAIN_WINDOW_LABEL) { 39 | match status { 40 | MacOSPanelStatus::Show => { 41 | panel.show(); 42 | } 43 | MacOSPanelStatus::Hide => { 44 | panel.order_out(None); 45 | } 46 | } 47 | } 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src-tauri/src/plugins/window/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | use tauri::{async_runtime::spawn, AppHandle, Manager, Runtime, WebviewWindow}; 2 | 3 | pub static MAIN_WINDOW_LABEL: &str = "main"; 4 | pub static PREFERENCE_WINDOW_LABEL: &str = "preference"; 5 | 6 | #[cfg(target_os = "macos")] 7 | mod macos; 8 | 9 | #[cfg(not(target_os = "macos"))] 10 | mod not_macos; 11 | 12 | #[cfg(target_os = "macos")] 13 | pub use macos::*; 14 | 15 | #[cfg(not(target_os = "macos"))] 16 | pub use not_macos::*; 17 | 18 | pub fn is_main_window(window: &WebviewWindow) -> bool { 19 | window.label() == MAIN_WINDOW_LABEL 20 | } 21 | 22 | fn shared_show_window(app_handle: &AppHandle, window: &WebviewWindow) { 23 | let _ = window.show(); 24 | let _ = window.unminimize(); 25 | let _ = window.set_focus(); 26 | 27 | let _ = app_handle; 28 | } 29 | 30 | fn shared_hide_window(app_handle: &AppHandle, window: &WebviewWindow) { 31 | let _ = window.hide(); 32 | 33 | let _ = app_handle; 34 | } 35 | 36 | pub fn show_main_window(app_handle: &AppHandle) { 37 | show_window_by_label(app_handle, MAIN_WINDOW_LABEL); 38 | } 39 | 40 | pub fn show_preference_window(app_handle: &AppHandle) { 41 | show_window_by_label(app_handle, PREFERENCE_WINDOW_LABEL); 42 | } 43 | 44 | fn show_window_by_label(app_handle: &AppHandle, label: &str) { 45 | if let Some(window) = app_handle.get_webview_window(label) { 46 | let app_handle_clone = app_handle.clone(); 47 | 48 | spawn(async move { 49 | show_window(app_handle_clone, window).await; 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src-tauri/src/plugins/window/src/commands/not_macos.rs: -------------------------------------------------------------------------------- 1 | use super::{shared_hide_window, shared_show_window}; 2 | use tauri::{command, AppHandle, Runtime, WebviewWindow}; 3 | 4 | #[command] 5 | pub async fn show_window(app_handle: AppHandle, window: WebviewWindow) { 6 | shared_show_window(&app_handle, &window); 7 | } 8 | 9 | #[command] 10 | pub async fn hide_window(app_handle: AppHandle, window: WebviewWindow) { 11 | shared_hide_window(&app_handle, &window); 12 | } 13 | -------------------------------------------------------------------------------- /src-tauri/src/plugins/window/src/lib.rs: -------------------------------------------------------------------------------- 1 | use tauri::{ 2 | generate_handler, 3 | plugin::{Builder, TauriPlugin}, 4 | Runtime, 5 | }; 6 | 7 | mod commands; 8 | 9 | pub use commands::*; 10 | 11 | pub fn init() -> TauriPlugin { 12 | Builder::new("custom-window") 13 | .invoke_handler(generate_handler![ 14 | commands::show_window, 15 | commands::hide_window, 16 | ]) 17 | .build() 18 | } 19 | -------------------------------------------------------------------------------- /src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "BongoCat", 4 | "version": "../package.json", 5 | "identifier": "com.ayangweb.BongoCat", 6 | "build": { 7 | "beforeDevCommand": "pnpm dev", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "pnpm build", 10 | "frontendDist": "../dist" 11 | }, 12 | "app": { 13 | "macOSPrivateApi": true, 14 | "windows": [ 15 | { 16 | "label": "main", 17 | "title": "BongoCat", 18 | "url": "index.html/#/", 19 | "width": 408, 20 | "height": 236, 21 | "minWidth": 204, 22 | "minHeight": 118, 23 | "shadow": false, 24 | "alwaysOnTop": true, 25 | "transparent": true, 26 | "decorations": false, 27 | "acceptFirstMouse": true, 28 | "skipTaskbar": true 29 | }, 30 | { 31 | "label": "preference", 32 | "title": "偏好设置", 33 | "url": "index.html/#/preference", 34 | "width": 600, 35 | "height": 450, 36 | "visible": false, 37 | "resizable": false, 38 | "maximizable": false, 39 | "titleBarStyle": "Overlay", 40 | "hiddenTitle": true 41 | } 42 | ], 43 | "security": { 44 | "csp": null, 45 | "dangerousDisableAssetCspModification": true, 46 | "assetProtocol": { 47 | "enable": true, 48 | "scope": { 49 | "allow": ["**/*"], 50 | "requireLiteralLeadingDot": false 51 | } 52 | } 53 | } 54 | }, 55 | "bundle": { 56 | "active": true, 57 | "category": "Game", 58 | "createUpdaterArtifacts": true, 59 | "targets": ["nsis", "dmg", "app", "appimage", "deb", "rpm"], 60 | "shortDescription": "BongoCat", 61 | "icon": [ 62 | "icons/32x32.png", 63 | "icons/128x128.png", 64 | "icons/128x128@2x.png", 65 | "icons/icon.icns", 66 | "icons/icon.ico" 67 | ], 68 | "resources": ["assets/tray.png", "assets/logo.png"] 69 | }, 70 | "plugins": { 71 | "updater": { 72 | "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEVBRjJFMzE3MjEwMUZEMTAKUldRUS9RRWhGK1B5NmdkemhKcUFrVjZBQXlzdExpakdWVEJDeU9XckVsbzV2cFIycVJOempWa2UK", 73 | "endpoints": [ 74 | "https://gh-proxy.com/github.com/ayangweb/BongoCat/releases/latest/download/latest.json" 75 | ] 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src-tauri/tauri.linux.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "identifier": "com.ayangweb.BongoCat", 3 | "bundle": { 4 | "linux": { 5 | "deb": { 6 | "depends": ["gstreamer1.0-plugins-good"], 7 | "desktopTemplate": "./BongoCat.desktop" 8 | }, 9 | "rpm": { 10 | "depends": ["gstreamer1-plugins-good"], 11 | "desktopTemplate": "./BongoCat.desktop" 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src-tauri/tauri.macos.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "identifier": "com.ayangweb.BongoCat", 3 | "bundle": { 4 | "resources": ["assets/tray.png", "assets/logo.png"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src-tauri/tauri.windows.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "identifier": "com.ayangweb.BongoCat", 3 | "bundle": { 4 | "windows": { 5 | "digestAlgorithm": "sha256", 6 | "nsis": { 7 | "languages": ["SimpChinese"], 8 | "installMode": "both" 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 78 | -------------------------------------------------------------------------------- /src/assets/css/global.scss: -------------------------------------------------------------------------------- 1 | html { 2 | --uno: select-none overscroll-none antialiased; 3 | 4 | color-scheme: light; 5 | 6 | &.dark { 7 | color-scheme: dark; 8 | } 9 | 10 | img { 11 | -webkit-user-drag: none; 12 | } 13 | 14 | button { 15 | outline: none !important; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/assets/images/keys/Alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Alt.png -------------------------------------------------------------------------------- /src/assets/images/keys/AltGr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/AltGr.png -------------------------------------------------------------------------------- /src/assets/images/keys/BackQuote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/BackQuote.png -------------------------------------------------------------------------------- /src/assets/images/keys/Backspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Backspace.png -------------------------------------------------------------------------------- /src/assets/images/keys/CapsLock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/CapsLock.png -------------------------------------------------------------------------------- /src/assets/images/keys/Control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Control.png -------------------------------------------------------------------------------- /src/assets/images/keys/ControlLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/ControlLeft.png -------------------------------------------------------------------------------- /src/assets/images/keys/ControlRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/ControlRight.png -------------------------------------------------------------------------------- /src/assets/images/keys/Delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Delete.png -------------------------------------------------------------------------------- /src/assets/images/keys/DownArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/DownArrow.png -------------------------------------------------------------------------------- /src/assets/images/keys/Escape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Escape.png -------------------------------------------------------------------------------- /src/assets/images/keys/Fn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Fn.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyA.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyB.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyC.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyD.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyE.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyF.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyG.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyH.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyI.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyJ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyJ.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyK.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyL.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyM.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyN.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyO.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyP.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyQ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyQ.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyR.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyS.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyT.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyU.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyU.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyV.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyV.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyW.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyX.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyY.png -------------------------------------------------------------------------------- /src/assets/images/keys/KeyZ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/KeyZ.png -------------------------------------------------------------------------------- /src/assets/images/keys/LeftArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/LeftArrow.png -------------------------------------------------------------------------------- /src/assets/images/keys/Meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Meta.png -------------------------------------------------------------------------------- /src/assets/images/keys/Num0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Num0.png -------------------------------------------------------------------------------- /src/assets/images/keys/Num1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Num1.png -------------------------------------------------------------------------------- /src/assets/images/keys/Num2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Num2.png -------------------------------------------------------------------------------- /src/assets/images/keys/Num3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Num3.png -------------------------------------------------------------------------------- /src/assets/images/keys/Num4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Num4.png -------------------------------------------------------------------------------- /src/assets/images/keys/Num5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Num5.png -------------------------------------------------------------------------------- /src/assets/images/keys/Num6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Num6.png -------------------------------------------------------------------------------- /src/assets/images/keys/Num7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Num7.png -------------------------------------------------------------------------------- /src/assets/images/keys/Num8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Num8.png -------------------------------------------------------------------------------- /src/assets/images/keys/Num9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Num9.png -------------------------------------------------------------------------------- /src/assets/images/keys/Return.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Return.png -------------------------------------------------------------------------------- /src/assets/images/keys/RightArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/RightArrow.png -------------------------------------------------------------------------------- /src/assets/images/keys/Shift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Shift.png -------------------------------------------------------------------------------- /src/assets/images/keys/ShiftLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/ShiftLeft.png -------------------------------------------------------------------------------- /src/assets/images/keys/ShiftRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/ShiftRight.png -------------------------------------------------------------------------------- /src/assets/images/keys/Slash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Slash.png -------------------------------------------------------------------------------- /src/assets/images/keys/Space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Space.png -------------------------------------------------------------------------------- /src/assets/images/keys/Tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/Tab.png -------------------------------------------------------------------------------- /src/assets/images/keys/UpArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/src/assets/images/keys/UpArrow.png -------------------------------------------------------------------------------- /src/components/pro-list-item/index.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 54 | -------------------------------------------------------------------------------- /src/components/pro-list/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | -------------------------------------------------------------------------------- /src/components/update-app/index.vue: -------------------------------------------------------------------------------- 1 | 108 | 109 | 144 | -------------------------------------------------------------------------------- /src/composables/useDevice.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue' 2 | 3 | import { useDebounceFn } from '@vueuse/core' 4 | import { reactive, ref } from 'vue' 5 | 6 | import { LISTEN_KEY } from '../constants' 7 | 8 | import { useTauriListen } from './useTauriListen' 9 | 10 | import { useCatStore } from '@/stores/cat' 11 | 12 | type MouseButtonValue = 'Left' | 'Right' | 'Middle' 13 | 14 | interface MouseButtonEvent { 15 | kind: 'MousePress' | 'MouseRelease' 16 | value: MouseButtonValue 17 | } 18 | 19 | interface MouseMoveValue { 20 | x: number 21 | y: number 22 | } 23 | 24 | interface MouseMoveEvent { 25 | kind: 'MouseMove' 26 | value: MouseMoveValue 27 | } 28 | 29 | interface KeyboardEvent { 30 | kind: 'KeyboardPress' | 'KeyboardRelease' 31 | value: string 32 | } 33 | 34 | type DeviceEvent = MouseButtonEvent | MouseMoveEvent | KeyboardEvent 35 | 36 | function getSupportKeys() { 37 | const files = import.meta.glob('../assets/images/keys/*.png', { eager: true }) 38 | 39 | return Object.keys(files).map((path) => { 40 | return path.split('/').pop()?.replace('.png', '') 41 | }) 42 | } 43 | 44 | const supportKeys = getSupportKeys() 45 | 46 | export function useDevice() { 47 | const pressedMouses = ref([]) 48 | const mousePosition = reactive({ x: 0, y: 0 }) 49 | const pressedKeys = ref([]) 50 | const catStore = useCatStore() 51 | 52 | const debounceCapsLockRelease = useDebounceFn(() => { 53 | handleRelease(pressedKeys, 'CapsLock') 54 | }, 100) 55 | 56 | const handlePress = (array: Ref, value?: T) => { 57 | if (!value) return 58 | 59 | array.value = [...new Set([...array.value, value])] 60 | } 61 | 62 | const handleRelease = (array: Ref, value?: T) => { 63 | if (!value) return 64 | 65 | array.value = array.value.filter(item => item !== value) 66 | } 67 | 68 | const normalizeKeyValue = (key: string) => { 69 | key = key.replace(/^(Meta).*/, '$1').replace(/F(\d+)/, 'Fn') 70 | 71 | const isInvalidArrowKey = key.endsWith('Arrow') && catStore.mode !== 'keyboard' 72 | const isUnsupportedKey = !supportKeys.includes(key) 73 | 74 | if (isInvalidArrowKey || isUnsupportedKey) return 75 | 76 | return key 77 | } 78 | 79 | useTauriListen(LISTEN_KEY.DEVICE_CHANGED, ({ payload }) => { 80 | const { kind, value } = payload 81 | 82 | if (value === 'CapsLock') { 83 | handlePress(pressedKeys, 'CapsLock') 84 | 85 | return debounceCapsLockRelease() 86 | } 87 | 88 | switch (kind) { 89 | case 'MousePress': 90 | return handlePress(pressedMouses, value) 91 | case 'MouseRelease': 92 | return handleRelease(pressedMouses, value) 93 | case 'MouseMove': 94 | return Object.assign(mousePosition, value) 95 | case 'KeyboardPress': 96 | return handlePress(pressedKeys, normalizeKeyValue(value)) 97 | case 'KeyboardRelease': 98 | return handleRelease(pressedKeys, normalizeKeyValue(value)) 99 | } 100 | }) 101 | 102 | return { 103 | pressedMouses, 104 | mousePosition, 105 | pressedKeys, 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/composables/useModel.ts: -------------------------------------------------------------------------------- 1 | import { LogicalSize } from '@tauri-apps/api/dpi' 2 | import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow' 3 | import { watch } from 'vue' 4 | 5 | import live2d from '../utils/live2d' 6 | import { getCursorMonitor } from '../utils/monitor' 7 | 8 | import { useCatStore } from '@/stores/cat' 9 | 10 | export function useModel() { 11 | const carStore = useCatStore() 12 | 13 | watch(() => carStore.mode, handleLoad) 14 | 15 | async function handleLoad() { 16 | const data = await live2d.load(`/models/${carStore.mode}/cat.model3.json`) 17 | 18 | handleResize() 19 | 20 | Object.assign(carStore, data) 21 | } 22 | 23 | function handleDestroy() { 24 | live2d.destroy() 25 | } 26 | 27 | async function handleResize() { 28 | if (!live2d.model) return 29 | 30 | const appWindow = getCurrentWebviewWindow() 31 | const { innerWidth } = window 32 | 33 | await appWindow.setSize( 34 | new LogicalSize({ 35 | width: innerWidth, 36 | height: innerWidth * (354 / 612), 37 | }), 38 | ) 39 | 40 | live2d.model?.scale.set(innerWidth / 612) 41 | } 42 | 43 | function handleKeyDown(value: string[]) { 44 | const hasArrowKey = value.some(key => key.endsWith('Arrow')) 45 | const hasNonArrowKey = value.some(key => !key.endsWith('Arrow')) 46 | 47 | live2d.setParameterValue('CatParamRightHandDown', hasArrowKey) 48 | live2d.setParameterValue('CatParamLeftHandDown', hasNonArrowKey) 49 | } 50 | 51 | async function handleMouseMove() { 52 | if (carStore.mode !== 'standard' || !live2d.model) return 53 | 54 | const monitor = await getCursorMonitor() 55 | 56 | if (!monitor) return 57 | 58 | const { size, cursorX, cursorY } = monitor 59 | const { width, height } = size 60 | 61 | const xRatio = cursorX / width 62 | const yRatio = cursorY / height 63 | 64 | const x = (xRatio * 60) - 30 65 | const y = (yRatio * 60) - 30 66 | 67 | live2d.setParameterValue('ParamMouseX', -x) 68 | live2d.setParameterValue('ParamMouseY', -y) 69 | live2d.setParameterValue('ParamAngleX', x) 70 | live2d.setParameterValue('ParamAngleY', -y) 71 | } 72 | 73 | function handleMouseDown(value: string[]) { 74 | const hasLeftDown = value.includes('Left') 75 | const hasRightDown = value.includes('Right') 76 | 77 | live2d.setParameterValue('ParamMouseLeftDown', hasLeftDown) 78 | live2d.setParameterValue('ParamMouseRightDown', hasRightDown) 79 | } 80 | 81 | return { 82 | handleLoad, 83 | handleDestroy, 84 | handleResize, 85 | handleKeyDown, 86 | handleMouseMove, 87 | handleMouseDown, 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/composables/useTauriListen.ts: -------------------------------------------------------------------------------- 1 | import { listen } from '@tauri-apps/api/event' 2 | import { noop } from '@vueuse/core' 3 | import { onMounted, onUnmounted, ref } from 'vue' 4 | 5 | export function useTauriListen(...args: Parameters>) { 6 | const unlisten = ref(noop) 7 | 8 | onMounted(async () => { 9 | unlisten.value = await listen(...args) 10 | }) 11 | 12 | onUnmounted(() => { 13 | unlisten.value() 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/composables/useThemeVars.ts: -------------------------------------------------------------------------------- 1 | import { theme } from 'ant-design-vue' 2 | import { dash } from 'radash' 3 | 4 | export function useThemeVars() { 5 | const { defaultAlgorithm, darkAlgorithm, defaultConfig } = theme 6 | 7 | const generateColorVars = () => { 8 | const { token } = defaultConfig 9 | 10 | const colors = [ 11 | defaultAlgorithm(token), 12 | darkAlgorithm(token), 13 | ] 14 | 15 | for (const [index, item] of colors.entries()) { 16 | const isDark = index !== 0 17 | const vars: Record = {} 18 | 19 | for (const [key, value] of Object.entries(item)) { 20 | vars[`--ant-${dash(key)}`] = value 21 | } 22 | 23 | const style = document.createElement('style') 24 | style.dataset.theme = isDark ? 'dark' : 'light' 25 | const selector = isDark ? 'html.dark' : ':root' 26 | const values = Object.entries(vars).map(([key, value]) => `${key}: ${value};`) 27 | 28 | style.innerHTML = `${selector}{\n${values.join('\n')}\n}` 29 | document.head.appendChild(style) 30 | } 31 | } 32 | 33 | return { 34 | generateColorVars, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/composables/useTray.ts: -------------------------------------------------------------------------------- 1 | import type { TrayIconOptions } from '@tauri-apps/api/tray' 2 | 3 | import { getName, getVersion } from '@tauri-apps/api/app' 4 | import { emit } from '@tauri-apps/api/event' 5 | import { CheckMenuItem, Menu, MenuItem, PredefinedMenuItem, Submenu } from '@tauri-apps/api/menu' 6 | import { resolveResource } from '@tauri-apps/api/path' 7 | import { TrayIcon } from '@tauri-apps/api/tray' 8 | import { openUrl } from '@tauri-apps/plugin-opener' 9 | import { exit, relaunch } from '@tauri-apps/plugin-process' 10 | import { ref, watch } from 'vue' 11 | 12 | import { GITHUB_LINK, LISTEN_KEY } from '../constants' 13 | import { hideWindow, showWindow } from '../plugins/window' 14 | import { isMac } from '../utils/platform' 15 | 16 | import { useCatStore } from '@/stores/cat' 17 | 18 | const TRAY_ID = 'BONGO_CAT_TRAY' 19 | 20 | export function useTray() { 21 | const visible = ref(true) 22 | const catStore = useCatStore() 23 | 24 | watch([visible, () => catStore.mode, () => catStore.penetrable], () => { 25 | updateTrayMenu() 26 | }) 27 | 28 | const createTray = async () => { 29 | const tray = await getTrayById() 30 | 31 | if (tray) return 32 | 33 | const appName = await getName() 34 | const appVersion = await getVersion() 35 | 36 | const menu = await getTrayMenu() 37 | 38 | const icon = await resolveResource('assets/tray.png') 39 | 40 | const options: TrayIconOptions = { 41 | menu, 42 | icon, 43 | id: TRAY_ID, 44 | tooltip: `${appName} v${appVersion}`, 45 | iconAsTemplate: false, 46 | menuOnLeftClick: true, 47 | } 48 | 49 | return TrayIcon.new(options) 50 | } 51 | 52 | const getTrayById = () => { 53 | return TrayIcon.getById(TRAY_ID) 54 | } 55 | 56 | const getTrayMenu = async () => { 57 | const appVersion = await getVersion() 58 | 59 | const items = await Promise.all([ 60 | MenuItem.new({ 61 | text: '偏好设置...', 62 | accelerator: isMac ? 'Cmd+,' : '', 63 | action: () => showWindow(), 64 | }), 65 | MenuItem.new({ 66 | text: visible.value ? '隐藏猫咪' : '显示猫咪', 67 | action: () => { 68 | if (visible.value) { 69 | hideWindow('main') 70 | } else { 71 | showWindow('main') 72 | } 73 | 74 | visible.value = !visible.value 75 | }, 76 | }), 77 | Submenu.new({ 78 | text: '猫咪模式', 79 | items: await Promise.all([ 80 | CheckMenuItem.new({ 81 | text: '标准模式', 82 | checked: catStore.mode === 'standard', 83 | action: () => { 84 | catStore.mode = 'standard' 85 | }, 86 | }), 87 | CheckMenuItem.new({ 88 | text: '键盘模式', 89 | checked: catStore.mode === 'keyboard', 90 | action: () => { 91 | catStore.mode = 'keyboard' 92 | }, 93 | }), 94 | ]), 95 | }), 96 | CheckMenuItem.new({ 97 | text: '窗口穿透', 98 | checked: catStore.penetrable, 99 | action: () => { 100 | catStore.penetrable = !catStore.penetrable 101 | }, 102 | }), 103 | PredefinedMenuItem.new({ item: 'Separator' }), 104 | MenuItem.new({ 105 | text: '检查更新', 106 | action: () => { 107 | showWindow() 108 | 109 | emit(LISTEN_KEY.UPDATE_APP) 110 | }, 111 | }), 112 | MenuItem.new({ 113 | text: '开源地址', 114 | action: () => openUrl(GITHUB_LINK), 115 | }), 116 | PredefinedMenuItem.new({ item: 'Separator' }), 117 | MenuItem.new({ 118 | text: `版本 ${appVersion}`, 119 | enabled: false, 120 | }), 121 | MenuItem.new({ 122 | text: '重启应用', 123 | action: relaunch, 124 | }), 125 | MenuItem.new({ 126 | text: '退出应用', 127 | accelerator: isMac ? 'Cmd+Q' : '', 128 | action: () => exit(0), 129 | }), 130 | ]) 131 | 132 | return Menu.new({ items }) 133 | } 134 | 135 | const updateTrayMenu = async () => { 136 | const tray = await getTrayById() 137 | 138 | if (!tray) return 139 | 140 | const menu = await getTrayMenu() 141 | 142 | tray.setMenu(menu) 143 | } 144 | 145 | return { 146 | createTray, 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/composables/useWindowState.ts: -------------------------------------------------------------------------------- 1 | import type { Event } from '@tauri-apps/api/event' 2 | 3 | import { PhysicalPosition, PhysicalSize } from '@tauri-apps/api/dpi' 4 | import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow' 5 | import { onMounted, ref } from 'vue' 6 | 7 | import { useAppStore } from '@/stores/app' 8 | 9 | export type WindowState = Record | undefined> 10 | 11 | const appWindow = getCurrentWebviewWindow() 12 | const { label } = appWindow 13 | 14 | export function useWindowState() { 15 | const appStore = useAppStore() 16 | const isRestored = ref(false) 17 | 18 | onMounted(() => { 19 | appWindow.onMoved(onChange) 20 | 21 | appWindow.onResized(onChange) 22 | }) 23 | 24 | const onChange = async (event: Event) => { 25 | const minimized = await appWindow.isMinimized() 26 | 27 | if (minimized) return 28 | 29 | appStore.windowState[label] ??= {} 30 | 31 | Object.assign(appStore.windowState[label], event.payload) 32 | } 33 | 34 | const restoreState = async () => { 35 | const { x, y, width, height } = appStore.windowState[label] ?? {} 36 | 37 | if (x && y) { 38 | await appWindow.setPosition(new PhysicalPosition(x, y)) 39 | } 40 | 41 | if (width && height) { 42 | await appWindow.setSize(new PhysicalSize(width, height)) 43 | } 44 | 45 | isRestored.value = true 46 | } 47 | 48 | return { 49 | isRestored, 50 | restoreState, 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/constants/index.ts: -------------------------------------------------------------------------------- 1 | export const GITHUB_LINK = 'https://github.com/ayangweb/BongoCat' 2 | 3 | export const LISTEN_KEY = { 4 | SHOW_WINDOW: 'show-window', 5 | HIDE_WINDOW: 'hide-window', 6 | DEVICE_CHANGED: 'device-changed', 7 | UPDATE_APP: 'update-app', 8 | } 9 | -------------------------------------------------------------------------------- /src/layouts/preference/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 36 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createPlugin } from '@tauri-store/pinia' 2 | import { createPinia } from 'pinia' 3 | import { createApp } from 'vue' 4 | 5 | import App from './App.vue' 6 | import router from './router' 7 | import 'virtual:uno.css' 8 | import 'ant-design-vue/dist/reset.css' 9 | import './assets/css/global.scss' 10 | 11 | const pinia = createPinia() 12 | pinia.use(createPlugin({ saveOnChange: true })) 13 | 14 | createApp(App).use(router).use(pinia).mount('#app') 15 | -------------------------------------------------------------------------------- /src/pages/about/index.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 47 | -------------------------------------------------------------------------------- /src/pages/cat/index.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 45 | -------------------------------------------------------------------------------- /src/pages/general/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | -------------------------------------------------------------------------------- /src/pages/main/index.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 72 | -------------------------------------------------------------------------------- /src/pages/model/index.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /src/plugins/window.ts: -------------------------------------------------------------------------------- 1 | import { invoke } from '@tauri-apps/api/core' 2 | import { emit } from '@tauri-apps/api/event' 3 | 4 | import { LISTEN_KEY } from '../constants' 5 | 6 | type WindowLabel = 'main' | 'preference' 7 | 8 | const COMMAND = { 9 | SHOW_WINDOW: 'plugin:custom-window|show_window', 10 | HIDE_WINDOW: 'plugin:custom-window|hide_window', 11 | } 12 | 13 | export function showWindow(label?: WindowLabel) { 14 | if (label) { 15 | emit(LISTEN_KEY.SHOW_WINDOW, label) 16 | } else { 17 | invoke(COMMAND.SHOW_WINDOW) 18 | } 19 | } 20 | 21 | export function hideWindow(label?: WindowLabel) { 22 | if (label) { 23 | emit(LISTEN_KEY.HIDE_WINDOW, label) 24 | } else { 25 | invoke(COMMAND.HIDE_WINDOW) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | // @unocss-include 2 | import type { RouteRecordRaw } from 'vue-router' 3 | 4 | import { createRouter, createWebHashHistory } from 'vue-router' 5 | 6 | import Preference from '../layouts/preference/index.vue' 7 | import General from '../pages/general/index.vue' 8 | import Main from '../pages/main/index.vue' 9 | 10 | export const preferenceRoutes: RouteRecordRaw[] = [ 11 | { 12 | path: 'cat', 13 | component: () => import('../pages/cat/index.vue'), 14 | meta: { 15 | title: '猫咪设置', 16 | icon: 'i-solar:cat-outline', 17 | }, 18 | }, 19 | { 20 | path: 'general', 21 | component: General, 22 | meta: { 23 | title: '通用设置', 24 | icon: 'i-solar:settings-outline', 25 | }, 26 | }, 27 | { 28 | path: 'model', 29 | component: () => import('../pages/model/index.vue'), 30 | meta: { 31 | title: '模型管理', 32 | icon: 'i-solar:magic-stick-3-outline', 33 | }, 34 | }, 35 | { 36 | path: 'about', 37 | component: () => import('../pages/about/index.vue'), 38 | meta: { 39 | title: '关于', 40 | icon: 'i-solar:info-circle-outline', 41 | }, 42 | }, 43 | ] 44 | 45 | const routes: Readonly = [ 46 | { 47 | path: '/', 48 | component: Main, 49 | }, 50 | { 51 | path: '/preference', 52 | component: Preference, 53 | redirect: '/preference/cat', 54 | children: preferenceRoutes, 55 | }, 56 | ] 57 | 58 | const router = createRouter({ 59 | history: createWebHashHistory(), 60 | routes, 61 | }) 62 | 63 | export default router 64 | -------------------------------------------------------------------------------- /src/stores/app.ts: -------------------------------------------------------------------------------- 1 | import type { WindowState } from '@/composables/useWindowState' 2 | 3 | import { getName, getVersion } from '@tauri-apps/api/app' 4 | import { defineStore } from 'pinia' 5 | import { onMounted, reactive, ref } from 'vue' 6 | 7 | export const useAppStore = defineStore('app', () => { 8 | const name = ref('') 9 | const version = ref('') 10 | const windowState = reactive({}) 11 | 12 | onMounted(async () => { 13 | name.value = await getName() 14 | version.value = await getVersion() 15 | }) 16 | 17 | return { 18 | name, 19 | version, 20 | windowState, 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /src/stores/cat.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { ref } from 'vue' 3 | 4 | export const useCatStore = defineStore('cat', () => { 5 | const mode = ref<'standard' | 'keyboard'>('standard') 6 | const penetrable = ref(false) 7 | const opacity = ref(100) 8 | const mirrorMode = ref(false) 9 | 10 | return { 11 | mode, 12 | penetrable, 13 | opacity, 14 | mirrorMode, 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /src/stores/general.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { ref } from 'vue' 3 | 4 | export const useGeneralStore = defineStore('general', () => { 5 | const autoCheckUpdate = ref(false) 6 | 7 | return { 8 | autoCheckUpdate, 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /src/stores/model.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { ref } from 'vue' 3 | 4 | interface Motion { 5 | Key: string 6 | Name: string 7 | File: string 8 | Sound?: string 9 | FadeInTime: number 10 | FadeOutTime: number 11 | } 12 | 13 | interface Expression { 14 | Key: string 15 | Name: string 16 | File: string 17 | } 18 | 19 | export const useModelStore = defineStore('model', () => { 20 | const motions = ref([]) 21 | const expressions = ref([]) 22 | 23 | return { 24 | motions, 25 | expressions, 26 | } 27 | }) 28 | -------------------------------------------------------------------------------- /src/utils/live2d.ts: -------------------------------------------------------------------------------- 1 | import { Live2DModel } from 'pixi-live2d-display' 2 | import { Application, Ticker } from 'pixi.js' 3 | 4 | Live2DModel.registerTicker(Ticker) 5 | 6 | class Live2d { 7 | private app: Application | null = null 8 | public model: Live2DModel | null = null 9 | 10 | constructor() { } 11 | 12 | private mount() { 13 | const view = document.getElementById('live2dCanvas') as HTMLCanvasElement 14 | 15 | this.app = new Application({ 16 | view, 17 | resizeTo: window, 18 | backgroundAlpha: 0, 19 | autoDensity: true, 20 | resolution: devicePixelRatio, 21 | }) 22 | } 23 | 24 | public async load(url: string) { 25 | if (!this.app) { 26 | this.mount() 27 | } 28 | 29 | const model = await Live2DModel.from(url) 30 | 31 | if (this.app?.stage.children.length) { 32 | this.app.stage.removeChildren() 33 | } 34 | 35 | this.app?.stage.addChild(model) 36 | 37 | const { definitions, expressionManager } = model.internalModel.motionManager 38 | 39 | this.model = model 40 | 41 | return { 42 | motions: Object.values(definitions).flat(), 43 | expressions: expressionManager?.definitions ?? [], 44 | } 45 | } 46 | 47 | public destroy() { 48 | this.model?.destroy() 49 | } 50 | 51 | public setParameterValue(id: string, value: number | boolean) { 52 | return this.model?.internalModel.coreModel.setParameterValueById(id, Number(value)) 53 | } 54 | } 55 | 56 | const live2d = new Live2d() 57 | 58 | export default live2d 59 | -------------------------------------------------------------------------------- /src/utils/monitor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | availableMonitors, 3 | cursorPosition, 4 | } from '@tauri-apps/api/window' 5 | 6 | export async function getCursorMonitor() { 7 | const monitors = await availableMonitors() 8 | 9 | if (!monitors.length) return 10 | 11 | const { x, y } = await cursorPosition() 12 | 13 | const monitor = monitors.find((monitor) => { 14 | const { position, size } = monitor 15 | 16 | const inX = x >= position.x && x <= position.x + size.width 17 | const inY = y >= position.y && y <= position.y + size.height 18 | 19 | return inX && inY 20 | }) 21 | 22 | if (!monitor) return 23 | 24 | return { 25 | ...monitor, 26 | cursorX: x, 27 | cursorY: y, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/platform.ts: -------------------------------------------------------------------------------- 1 | import { platform } from '@tauri-apps/plugin-os' 2 | 3 | export const isMac = platform() === 'macos' 4 | 5 | export const isWindows = platform() === 'windows' 6 | 7 | export const isLinux = platform() === 'linux' 8 | -------------------------------------------------------------------------------- /src/utils/tauri.ts: -------------------------------------------------------------------------------- 1 | export function getMainWebviewWindow() { 2 | } 3 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue' 5 | 6 | const component: DefineComponent 7 | export default component 8 | } 9 | -------------------------------------------------------------------------------- /static/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/474bc5202ab176afc54a15dad94fc6e05be6d073/static/demo.gif -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "jsx": "preserve", 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "useDefineForClassFields": true, 7 | 8 | "baseUrl": ".", 9 | "module": "ESNext", 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "paths": { 13 | "@/*": ["src/*"] 14 | }, 15 | "resolveJsonModule": true, 16 | "allowImportingTsExtensions": true, 17 | 18 | /* Linting */ 19 | "strict": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "noEmit": true, 24 | "isolatedModules": true, 25 | "skipLibCheck": true 26 | }, 27 | "references": [{ "path": "./tsconfig.node.json" }], 28 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 29 | } 30 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "allowSyntheticDefaultImports": true, 7 | "skipLibCheck": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /uno.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig, 3 | presetIcons, 4 | presetWind3, 5 | transformerDirectives, 6 | transformerVariantGroup, 7 | } from 'unocss' 8 | 9 | export default defineConfig({ 10 | presets: [ 11 | presetWind3(), 12 | presetIcons(), 13 | ], 14 | transformers: [ 15 | transformerVariantGroup(), 16 | transformerDirectives({ 17 | applyVariable: ['--uno'], 18 | }), 19 | ], 20 | shortcuts: [ 21 | [/^bg-color-(\d+)$/, ([, d]) => `bg-bg-${d}`], 22 | [/^text-color-(\d+)$/, ([, d]) => `text-text-${d}`], 23 | [/^b-color-(\d+)$/, ([, d]) => `b-border-${d}`], 24 | [/^(.*)-primary-(\d+)$/, ([, s, d]) => `${s}-[var(--ant-blue-${d})]`], 25 | ], 26 | theme: { 27 | colors: { 28 | 'bg-1': 'var(--ant-color-bg-layout)', 29 | 'bg-2': 'var(--ant-color-bg-container)', 30 | 'bg-3': 'var(--ant-color-bg-elevated)', 31 | 'bg-4': 'var(--ant-color-bg-spotlight)', 32 | 'bg-5': 'var(--ant-color-fill)', 33 | 'bg-6': 'var(--ant-color-fill-secondary)', 34 | 'bg-7': 'var(--ant-color-fill-tertiary)', 35 | 'bg-8': 'var(--ant-color-fill-quaternary)', 36 | 'text-1': 'var(--ant-color-text)', 37 | 'text-2': 'var(--ant-color-text-secondary)', 38 | 'text-3': 'var(--ant-color-text-tertiary)', 39 | 'text-4': 'var(--ant-color-text-quaternary)', 40 | 'border-1': 'var(--ant-color-border)', 41 | 'border-2': 'var(--ant-color-border-secondary)', 42 | 'primary': 'var(--ant-blue)', 43 | }, 44 | }, 45 | }) 46 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path' 2 | import { env } from 'node:process' 3 | 4 | import vue from '@vitejs/plugin-vue' 5 | import UnoCSS from 'unocss/vite' 6 | import { defineConfig } from 'vite' 7 | 8 | const host = env.TAURI_DEV_HOST 9 | 10 | // https://vitejs.dev/config/ 11 | export default defineConfig(async () => ({ 12 | plugins: [vue(), UnoCSS()], 13 | resolve: { 14 | alias: { 15 | '@': resolve(__dirname, 'src'), 16 | }, 17 | }, 18 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 19 | // 20 | // 1. prevent vite from obscuring rust errors 21 | clearScreen: false, 22 | // 2. tauri expects a fixed port, fail if that port is not available 23 | server: { 24 | port: 1420, 25 | strictPort: true, 26 | host: host || false, 27 | hmr: host 28 | ? { 29 | protocol: 'ws', 30 | host, 31 | port: 1421, 32 | } 33 | : undefined, 34 | watch: { 35 | // 3. tell vite to ignore watching `src-tauri` 36 | ignored: ['**/src-tauri/**'], 37 | }, 38 | }, 39 | })) 40 | --------------------------------------------------------------------------------