├── .commitlintrc ├── .github ├── CONTRIBUTING.md ├── DOWNLOAD_GUIDE.md ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml └── workflows │ ├── release.yml │ └── sync-to-gitee.yml ├── .gitignore ├── .release-it.ts ├── .vscode ├── extensions.json └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── eslint.config.ts ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public ├── js │ ├── live2d.min.js │ └── live2dcubismcore.min.js └── logo.png ├── scripts ├── buildIcon.ts └── release.ts ├── src-tauri ├── .gitignore ├── BongoCat.desktop ├── Cargo.toml ├── assets │ ├── logo-mac.png │ ├── logo.png │ ├── 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 │ │ │ └── resources │ │ │ │ ├── background.png │ │ │ │ ├── cover.png │ │ │ │ ├── left-keys │ │ │ │ ├── Alt.png │ │ │ │ ├── AltGr.png │ │ │ │ ├── BackQuote.png │ │ │ │ ├── Backspace.png │ │ │ │ ├── CapsLock.png │ │ │ │ ├── Control.png │ │ │ │ ├── ControlLeft.png │ │ │ │ ├── ControlRight.png │ │ │ │ ├── Delete.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 │ │ │ │ ├── Meta.png │ │ │ │ ├── Num0.png │ │ │ │ ├── Num1.png │ │ │ │ ├── Num2.png │ │ │ │ ├── Num3.png │ │ │ │ ├── Num4.png │ │ │ │ ├── Num5.png │ │ │ │ ├── Num6.png │ │ │ │ ├── Num7.png │ │ │ │ ├── Num8.png │ │ │ │ ├── Num9.png │ │ │ │ ├── Return.png │ │ │ │ ├── Shift.png │ │ │ │ ├── ShiftLeft.png │ │ │ │ ├── ShiftRight.png │ │ │ │ ├── Slash.png │ │ │ │ ├── Space.png │ │ │ │ └── Tab.png │ │ │ │ └── right-keys │ │ │ │ ├── DownArrow.png │ │ │ │ ├── LeftArrow.png │ │ │ │ ├── RightArrow.png │ │ │ │ └── UpArrow.png │ │ └── 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 │ │ │ └── resources │ │ │ ├── background.png │ │ │ ├── cover.png │ │ │ └── left-keys │ │ │ ├── Alt.png │ │ │ ├── AltGr.png │ │ │ ├── BackQuote.png │ │ │ ├── Backspace.png │ │ │ ├── CapsLock.png │ │ │ ├── Control.png │ │ │ ├── ControlLeft.png │ │ │ ├── ControlRight.png │ │ │ ├── Delete.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 │ │ │ ├── Meta.png │ │ │ ├── Num0.png │ │ │ ├── Num1.png │ │ │ ├── Num2.png │ │ │ ├── Num3.png │ │ │ ├── Num4.png │ │ │ ├── Num5.png │ │ │ ├── Num6.png │ │ │ ├── Num7.png │ │ │ ├── Num8.png │ │ │ ├── Num9.png │ │ │ ├── Return.png │ │ │ ├── Shift.png │ │ │ ├── ShiftLeft.png │ │ │ ├── ShiftRight.png │ │ │ ├── Slash.png │ │ │ ├── Space.png │ │ │ └── Tab.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 │ └── utils │ │ ├── fs_extra.rs │ │ └── mod.rs ├── tauri.conf.json ├── tauri.linux.conf.json ├── tauri.macos.conf.json └── tauri.windows.conf.json ├── src ├── App.vue ├── assets │ └── css │ │ └── global.scss ├── components │ ├── pro-list-item │ │ └── index.vue │ ├── pro-list │ │ └── index.vue │ └── update-app │ │ └── index.vue ├── composables │ ├── useDevice.ts │ ├── useModel.ts │ ├── useSharedMenu.ts │ ├── useTauriListen.ts │ ├── useThemeVars.ts │ ├── useTray.ts │ └── useWindowState.ts ├── constants │ └── index.ts ├── main.ts ├── pages │ ├── main │ │ └── index.vue │ └── preference │ │ ├── components │ │ ├── about │ │ │ └── index.vue │ │ ├── cat │ │ │ └── index.vue │ │ ├── general │ │ │ ├── components │ │ │ │ └── macos-permissions │ │ │ │ │ └── index.vue │ │ │ └── index.vue │ │ └── model │ │ │ ├── components │ │ │ ├── float-menu │ │ │ │ └── index.vue │ │ │ └── upload │ │ │ │ └── index.vue │ │ │ └── index.vue │ │ └── index.vue ├── plugins │ └── window.ts ├── router │ └── index.ts ├── stores │ ├── app.ts │ ├── cat.ts │ ├── general.ts │ └── model.ts ├── utils │ ├── dom.ts │ ├── is.ts │ ├── live2d.ts │ ├── monitor.ts │ ├── path.ts │ └── platform.ts └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json ├── uno.config.ts └── vite.config.ts /.commitlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@commitlint/config-conventional" 3 | } 4 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 贡献指南 2 | 3 | 非常感谢您对 BongoCat 的关注和贡献!在您提交贡献之前,请先花一些时间阅读以下指南,以确保您的贡献能够顺利进行。 4 | 5 | ## 透明的开发 6 | 7 | 所有工作都在 GitHub 上公开进行。无论是核心团队成员还是外部贡献者的 Pull Request,都需要经过相同的 review 流程。 8 | 9 | ## 提交 Issue 10 | 11 | 我们使用 [Github Issues](https://github.com/ayangweb/BongoCat/issues) 进行 Bug 报告和新 Feature 建议。在提交 Issue 之前,请确保已经搜索过类似的问题,因为它们可能已经得到解答或正在被修复。对于 Bug 报告,请包含可用于重现问题的完整步骤。对于新 Feature 建议,请指出你想要的更改以及期望的行为。 12 | 13 | ## 提交 Pull Request 14 | 15 | ### 共建流程 16 | 17 | - 认领 issue:在 Github 建立 Issue 并认领(或直接认领已有 Issue),告知大家自己正在修复,避免重复工作。 18 | - 项目开发:在完成准备工作后,进行 Bug 修复或功能开发。 19 | - 提交 PR。 20 | 21 | ### 准备工作 22 | 23 | - [Rust](https://v2.tauri.app/start/prerequisites/): 请自行根据官网步骤安装 rust 环境。 24 | - [Node.js](https://nodejs.org/en/): 用于运行项目。 25 | - [Pnpm](https://pnpm.io/):本项目使用 Pnpm 进行包管理。 26 | 27 | ### 下载依赖 28 | 29 | ```shell 30 | pnpm install 31 | ``` 32 | 33 | ### 启动应用 34 | 35 | ```shell 36 | pnpm tauri dev 37 | ``` 38 | 39 | ### 打包应用 40 | 41 | > 如果需要打包后进行调试,请在以下命令后面加上 `--debug` 42 | 43 | ```shell 44 | pnpm tauri build 45 | ``` 46 | 47 | ## Commit 指南 48 | 49 | Commit messages 请遵循[conventional-changelog 标准](https://www.conventionalcommits.org/en/v1.0.0/)。 50 | 51 | ### Commit 类型 52 | 53 | 以下是 commit 类型列表: 54 | 55 | - feat: 新特性或功能 56 | - fix: 缺陷修复 57 | - docs: 文档更新 58 | - style: 代码风格更新 59 | - refactor: 代码重构,不引入新功能和缺陷修复 60 | - perf: 性能优化 61 | - chore: 其他提交 62 | 63 | 期待您的参与,让我们一起使 BongoCat 变得更好! 64 | -------------------------------------------------------------------------------- /.github/DOWNLOAD_GUIDE.md: -------------------------------------------------------------------------------- 1 | # 下载指南 2 | 3 | ## 系统要求 4 | 5 | - macOS 12 或更高版本。 6 | - Windows 10 或更高版本。 7 | - Linux 带有 X11 环境。 8 | 9 | ## macOS 10 | 11 | ### 手动下载 12 | 13 | - Apple Silicon:下载 `BongoCat_aarch64.dmg` 14 | - Intel Chip:下载 `BongoCat_x64.dmg` 15 | 16 | ### Homebrew 下载 17 | 18 | 1. 添加 BongoCat 的 tap 源: 19 | 20 | ```bash 21 | brew tap ayangweb/BongoCat 22 | ``` 23 | 24 | 2. 安装: 25 | 26 | ```bash 27 | brew install bongo-cat 28 | ``` 29 | 30 | 3. 更新: 31 | 32 | ```bash 33 | brew upgrade bongo-cat 34 | ``` 35 | 36 | 4. 卸载: 37 | 38 | ```bash 39 | brew uninstall --cask bongo-cat 40 | 41 | brew untap ayangweb/BongoCat 42 | ``` 43 | 44 | ## Windows 45 | 46 | - 64 位系统:下载 `BongoCat_x64.exe` 47 | - 32 位系统:下载 `BongoCat_x86.exe` 48 | - ARM64 架构:下载 `BongoCat_arm64.exe` 49 | 50 | ## Linux(X11) 51 | 52 | ### 手动下载 53 | 54 | - 64 位系统: 55 | - Debian / Ubuntu:下载 `BongoCat_amd64.deb` 56 | - Fedora / RHEL:下载 `BongoCat_x86_64.rpm` 57 | - 通用版本:下载 `BongoCat_amd64.AppImage` 58 | - ARM64 架构: 59 | - Debian / Ubuntu:下载 `BongoCat_arm64.deb` 60 | - Fedora / RHEL:下载 `BongoCat_aarch64.rpm` 61 | - 通用版本:下载 `BongoCat_aarch64.AppImage` 62 | 63 | ### AUR 下载 64 | 65 | - Manjaro / ArchLinux: `yay -S bongo-cat` 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug 报告 2 | title: '[bug] ' 3 | description: 报告一个 Bug 4 | labels: 🐞 bug 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | ## 温馨提示 10 | 1. 请先查阅现有的 [issues](https://github.com/ayangweb/BongoCat/issues)。 11 | 2. 请确保你使用的是[最新版本](https://github.com/ayangweb/BongoCat/releases/latest)。 12 | 3. 请确保该问题不是由其他软件引起的。 13 | 4. 请始终保持友好与尊重,感谢你的理解与配合。 14 | 15 | - type: textarea 16 | id: description 17 | attributes: 18 | label: 描述 Bug 19 | description: 请详细描述 Bug 并提供截图或视频以帮助我们更好地理解问题。 20 | validations: 21 | required: true 22 | 23 | - type: textarea 24 | id: reproduction 25 | attributes: 26 | label: 重现步骤 27 | description: 请详细列出重现问题的步骤,并附带截图或视频。 28 | 29 | - type: textarea 30 | id: expected-behavior 31 | attributes: 32 | label: 预期行为 33 | description: 请描述你期望发生的行为。 34 | 35 | - type: textarea 36 | id: info 37 | attributes: 38 | render: text 39 | label: 软件信息 40 | description: 请前往偏好设置窗口的「关于 > 关于软件 > 软件信息」复制软件信息。 41 | validations: 42 | required: true 43 | 44 | - type: textarea 45 | id: context 46 | attributes: 47 | label: 附加信息 48 | description: 请在此提供有关该问题的其他相关信息,帮助我们更全面地理解问题。 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: 💡 功能请求 2 | title: '[feat] ' 3 | description: 提出一个想法 4 | labels: 💡 feature request 5 | body: 6 | - type: textarea 7 | id: problem 8 | attributes: 9 | label: 描述问题 10 | description: 请清晰地描述此功能将解决的具体问题。 11 | validations: 12 | required: true 13 | 14 | - type: textarea 15 | id: solution 16 | attributes: 17 | label: 描述您希望的解决方案 18 | description: 请清晰地描述您期望的变更或改进。 19 | validations: 20 | required: true 21 | 22 | - type: textarea 23 | id: alternatives 24 | attributes: 25 | label: 考虑的替代方案 26 | description: 提供您考虑过的其他替代解决方案。 27 | 28 | - type: textarea 29 | id: context 30 | attributes: 31 | label: 附加信息 32 | description: 请在此提供有关该问题的其他相关信息,帮助我们更全面地理解问题。 33 | -------------------------------------------------------------------------------- /.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 | - platform: ubuntu-22.04-arm 54 | target: aarch64-unknown-linux-gnu 55 | 56 | runs-on: ${{ matrix.platform }} 57 | steps: 58 | - name: Checkout repository 59 | uses: actions/checkout@v4 60 | - name: Setup node 61 | uses: actions/setup-node@v4 62 | with: 63 | node-version: 20 64 | - uses: pnpm/action-setup@v3 65 | with: 66 | version: latest 67 | 68 | - name: Install rust target 69 | run: rustup target add ${{ matrix.target }} 70 | 71 | - name: Install dependencies (ubuntu only) 72 | if: startsWith(matrix.platform, 'ubuntu') 73 | run: | 74 | sudo apt-get update 75 | sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf xdg-utils 76 | 77 | - name: Install Rust stable 78 | uses: dtolnay/rust-toolchain@stable 79 | 80 | - name: Rust cache 81 | uses: swatinem/rust-cache@v2 82 | with: 83 | workspaces: target 84 | 85 | - name: Sync node version and setup cache 86 | uses: actions/setup-node@v4 87 | with: 88 | node-version: 20 89 | cache: pnpm 90 | 91 | - name: Install app dependencies and build web 92 | run: pnpm install --frozen-lockfile 93 | 94 | - name: Build the app 95 | uses: tauri-apps/tauri-action@v0 96 | env: 97 | CI: false 98 | PLATFORM: ${{ matrix.platform }} 99 | GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} 100 | TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} 101 | TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} 102 | with: 103 | tagName: ${{ github.ref_name }} 104 | releaseName: BongoCat ${{ needs.create-release.outputs.APP_VERSION }} 105 | releaseBody: '' 106 | releaseDraft: true 107 | prerelease: false 108 | args: --target ${{ matrix.target }} 109 | -------------------------------------------------------------------------------- /.github/workflows/sync-to-gitee.yml: -------------------------------------------------------------------------------- 1 | name: Sync Github Repos To Gitee 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | repo-sync: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Sync Github Repos To Gitee 12 | uses: Yikun/hub-mirror-action@master 13 | with: 14 | src: github/ayangweb 15 | dst: gitee/ayangweb 16 | dst_key: ${{ secrets.GITEE_PRIVATE_KEY }} 17 | dst_token: ${{ secrets.GITEE_TOKEN }} 18 | static_list: BongoCat 19 | force_update: true 20 | -------------------------------------------------------------------------------- /.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 | "eslint.format.enable": true, 6 | 7 | // Auto fix 8 | "editor.codeActionsOnSave": { 9 | "source.fixAll.eslint": "explicit", 10 | "source.organizeImports": "never" 11 | }, 12 | 13 | // Silent the stylistic rules in you IDE, but still auto fix them 14 | "eslint.rules.customizations": [ 15 | { "rule": "style/*", "severity": "off", "fixable": true }, 16 | { "rule": "format/*", "severity": "off", "fixable": true }, 17 | { "rule": "*-indent", "severity": "off", "fixable": true }, 18 | { "rule": "*-spacing", "severity": "off", "fixable": true }, 19 | { "rule": "*-spaces", "severity": "off", "fixable": true }, 20 | { "rule": "*-order", "severity": "off", "fixable": true }, 21 | { "rule": "*-dangle", "severity": "off", "fixable": true }, 22 | { "rule": "*-newline", "severity": "off", "fixable": true }, 23 | { "rule": "*quotes", "severity": "off", "fixable": true }, 24 | { "rule": "*semi", "severity": "off", "fixable": true } 25 | ], 26 | 27 | // Enable eslint for all supported languages 28 | "eslint.validate": [ 29 | "javascript", 30 | "javascriptreact", 31 | "typescript", 32 | "typescriptreact", 33 | "vue", 34 | "html", 35 | "markdown", 36 | "json", 37 | "json5", 38 | "jsonc", 39 | "yaml", 40 | "toml", 41 | "xml", 42 | "gql", 43 | "graphql", 44 | "astro", 45 | "svelte", 46 | "css", 47 | "less", 48 | "scss", 49 | "pcss", 50 | "postcss" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ "src-tauri" ] 4 | 5 | [profile.release] 6 | strip = true 7 | opt-level = 3 8 | codegen-units = 1 9 | panic = "abort" 10 | debug-assertions = false 11 | overflow-checks = false 12 | lto = true 13 | 14 | [workspace.dependencies] 15 | tauri = "2" 16 | serde = "1" 17 | serde_json = "1" 18 | fs_extra = "1" 19 | tauri-plugin = { version = "2", features = [ "build" ] } 20 | tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" } 21 | tauri-plugin-custom-window = { path = "./src-tauri/src/plugins/window" } 22 | -------------------------------------------------------------------------------- /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?custom_description=&description=1&font=Source+Code+Pro&forks=1&issues=1&logo=https%3A%2F%2Fgithub.com%2Fayangweb%2FBongoCat%2Fblob%2Fmaster%2Fsrc-tauri%2Fassets%2Flogo-mac.png%3Fraw%3Dtrue&name=1&owner=1&pattern=Floating+Cogs&pulls=1&stargazers=1&theme=Auto) 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 | 47 | 48 | Star History Chart 49 | 50 | 51 |

52 |
53 | 54 | | macOS | Window | Linux(x11) | 55 | | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | 56 | | ![macOS](https://github.com/user-attachments/assets/e932b022-1472-4bbd-87ef-4a8ea374890a) | ![Windows](https://github.com/user-attachments/assets/79a4652e-0d14-412d-a274-4ccdd825d7c6) | ![Linux](https://github.com/user-attachments/assets/fd069c12-d12d-423b-b792-98b5926a7f09) | 57 | 58 | ## 开发背景 59 | 60 | 本项目的灵感来源于 [MMmmmoko](https://github.com/MMmmmoko) 大佬开发的 [Bongo-Cat-Mver](https://github.com/MMmmmoko/Bongo-Cat-Mver)。它以独特的猫咪互动功能深受用户喜爱,但仅支持 Windows 平台。作为一名深度 macOS 用户,我特别希望在自己的设备上也能使用这款可爱的猫咪,于是我决定开发一个适配 macOS 的版本。 61 | 62 | 同时,得益于 [Tauri](https://github.com/tauri-apps/tauri) 强大的跨平台能力,本项目不仅支持 macOS,还兼容 Windows 和 Linux(x11),让更多的用户都能与这只可爱的猫咪互动! 63 | 64 | ## 下载 65 | 66 | - [夸克网盘](https://pan.quark.cn/s/70f2f2663ce1) 67 | - [GitHub Releases](https://github.com/ayangweb/BongoCat/releases) 68 | 69 | > 不确定下载哪一个?请查阅[下载指南](.github/DOWNLOAD_GUIDE.md)。 70 | 71 | ## 功能介绍 72 | 73 | - 适配 macOS、Windows 和 Linux(x11)。 74 | - 根据据键盘或鼠标操作,同步移动鼠标或敲击键盘。 75 | - 支持导入自定义模型,自由打造专属猫咪形象。 76 | - 完全开源,代码公开透明,绝不收集任何用户数据。 77 | - 支持离线运行,无需联网,保护用户隐私。 78 | 79 | ## 更多模型 80 | 81 | 你可以在这个仓库中探索、下载更多猫咪模型,或提交你的创作,与大家一起分享: 82 | 83 | 📦 [Awesome-BongoCat](https://github.com/ayangweb/Awesome-BongoCat) 84 | 85 | ## 社区交流 86 | 87 | 88 | 89 | 90 | 91 | QQ Group 92 | 93 | 94 | 95 | ## 贡献指南 96 | 97 | 感谢大家为 BongoCat 做出的宝贵贡献!如果你也希望为 BongoCat 做出贡献,请查阅[贡献指南](.github/CONTRIBUTING.md)。 98 | 99 | 100 | 101 | 102 | 103 | ## 历史星标 104 | 105 | 106 | 107 | 108 | 109 | Star History Chart 110 | 111 | 112 | -------------------------------------------------------------------------------- /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 | 'vue/max-attributes-per-line': 'error', 25 | }, 26 | ignores: ['**/*.toml'], 27 | }) 28 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | BongoCat 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bongo-cat", 3 | "type": "module", 4 | "version": "0.5.0", 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 | "@ant-design/icons-vue": "^7.0.1", 25 | "@tauri-apps/api": "^2.5.0", 26 | "@tauri-apps/plugin-autostart": "~2.3.0", 27 | "@tauri-apps/plugin-clipboard-manager": "~2.2.2", 28 | "@tauri-apps/plugin-dialog": "~2.2.2", 29 | "@tauri-apps/plugin-fs": "~2.3.0", 30 | "@tauri-apps/plugin-log": "~2.3.1", 31 | "@tauri-apps/plugin-opener": "~2.2.7", 32 | "@tauri-apps/plugin-os": "^2.2.1", 33 | "@tauri-apps/plugin-process": "^2.2.1", 34 | "@tauri-apps/plugin-updater": "~2.7.1", 35 | "@tauri-store/pinia": "^3.4.0", 36 | "@vueuse/core": "^13.2.0", 37 | "ant-design-vue": "^4.2.6", 38 | "dayjs": "^1.11.13", 39 | "es-toolkit": "^1.38.0", 40 | "is-url": "^1.2.4", 41 | "nanoid": "^5.1.5", 42 | "pinia": "^3.0.2", 43 | "pixi-live2d-display": "^0.4.0", 44 | "pixi.js": "^6.5.10", 45 | "tauri-plugin-macos-permissions-api": "^2.3.0", 46 | "vue": "^3.5.14", 47 | "vue-markdown-render": "^2.2.1", 48 | "vue-router": "^4.5.1" 49 | }, 50 | "devDependencies": { 51 | "@antfu/eslint-config": "^4.13.2", 52 | "@commitlint/cli": "^19.8.1", 53 | "@commitlint/config-conventional": "^19.8.1", 54 | "@iconify-json/iconamoon": "^1.2.2", 55 | "@iconify-json/solar": "^1.2.2", 56 | "@tauri-apps/cli": "^2.5.0", 57 | "@types/is-url": "^1.2.32", 58 | "@types/node": "^22.15.21", 59 | "@unocss/eslint-plugin": "^66.1.2", 60 | "@vitejs/plugin-vue": "^5.2.4", 61 | "eslint": "^9.27.0", 62 | "eslint-plugin-format": "^1.0.1", 63 | "lint-staged": "^15.5.2", 64 | "npm-run-all": "^4.1.5", 65 | "release-it": "^18.1.2", 66 | "sass": "^1.89.0", 67 | "simple-git-hooks": "^2.13.0", 68 | "tsx": "^4.19.4", 69 | "typescript": "~5.6.3", 70 | "unocss": "66.1.0-beta.7", 71 | "vite": "^6.3.5" 72 | }, 73 | "simple-git-hooks": { 74 | "commit-msg": "npx --no-install commitlint -e", 75 | "pre-commit": "npx lint-staged" 76 | }, 77 | "lint-staged": { 78 | "*": "eslint --fix" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/public/logo.png -------------------------------------------------------------------------------- /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.5.0" 4 | description = "A Tauri App" 5 | authors = [ "ayangweb" ] 6 | edition = "2024" 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 = { git = "https://github.com/ayangweb/rdev" } 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 | tauri-plugin-autostart = "2" 35 | tauri-plugin-macos-permissions = "2" 36 | tauri-plugin-dialog = "2" 37 | tauri-plugin-fs = "2" 38 | fs_extra = "1" 39 | tauri-plugin-clipboard-manager = "2" 40 | 41 | [target."cfg(target_os = \"macos\")".dependencies] 42 | tauri-nspanel.workspace = true 43 | 44 | [features] 45 | cargo-clippy = [] 46 | -------------------------------------------------------------------------------- /src-tauri/assets/logo-mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/logo-mac.png -------------------------------------------------------------------------------- /src-tauri/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/logo.png -------------------------------------------------------------------------------- /src-tauri/assets/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": "默认喵", 14 | "File": "live2d_expression0.exp3.json" 15 | }, 16 | { 17 | "Name": "社会喵", 18 | "File": "live2d_expression1.exp3.json", 19 | "Description": "喵喵我叼根小烟,耍个帅气俏皮的wink~超有范儿!但直播里用这招,怕是会让铲屎官和平台瞪大眼,本喵还是低调点,偷偷耍酷好啦!" 20 | }, 21 | { 22 | "Name": "天使喵", 23 | "File": "live2d_expression2.exp3.json" 24 | } 25 | ], 26 | "Motions": { 27 | "CAT_motion": [ 28 | { 29 | "Name": "雷霆喵", 30 | "File": "live2d_motion1.motion3.json", 31 | "Sound": "live2d_motion1.flac", 32 | "FadeInTime": 0, 33 | "FadeOutTime": 0 34 | }, 35 | { 36 | "Name": "摇摆喵", 37 | "File": "live2d_motion2.motion3.json", 38 | "FadeInTime": 0, 39 | "FadeOutTime": 0 40 | } 41 | ] 42 | } 43 | }, 44 | "Groups": [ 45 | { 46 | "Target": "Parameter", 47 | "Name": "EyeBlink", 48 | "Ids": [ 49 | "ParamEyeLOpen", 50 | "ParamEyeROpen" 51 | ] 52 | }, 53 | { 54 | "Target": "Parameter", 55 | "Name": "LipSync", 56 | "Ids": [] 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/demomodel2.1024/texture_00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/demomodel2.1024/texture_00.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/demomodel2.1024/texture_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/demomodel2.1024/texture_01.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/demomodel2.1024/texture_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/demomodel2.1024/texture_02.png -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/demomodel2.moc3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/demomodel2.moc3 -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/live2d_expression0.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "Parameters": [] 4 | } 5 | -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/live2d_motion1.flac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/live2d_motion1.flac -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/background.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/cover.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Alt.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/AltGr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/AltGr.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/BackQuote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/BackQuote.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Backspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Backspace.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/CapsLock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/CapsLock.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Control.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/ControlLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/ControlLeft.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/ControlRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/ControlRight.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Delete.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Escape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Escape.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Fn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Fn.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyA.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyB.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyC.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyD.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyE.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyF.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyG.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyH.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyI.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyJ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyJ.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyK.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyL.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyM.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyN.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyO.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyP.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyQ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyQ.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyR.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyS.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyT.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyU.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyU.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyV.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyV.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyW.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyX.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyY.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/KeyZ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/KeyZ.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Meta.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Num0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Num0.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Num1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Num1.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Num2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Num2.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Num3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Num3.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Num4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Num4.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Num5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Num5.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Num6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Num6.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Num7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Num7.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Num8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Num8.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Num9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Num9.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Return.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Return.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Shift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Shift.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/ShiftLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/ShiftLeft.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/ShiftRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/ShiftRight.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Slash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Slash.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Space.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/left-keys/Tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/left-keys/Tab.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/right-keys/DownArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/right-keys/DownArrow.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/right-keys/LeftArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/right-keys/LeftArrow.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/right-keys/RightArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/right-keys/RightArrow.png -------------------------------------------------------------------------------- /src-tauri/assets/models/keyboard/resources/right-keys/UpArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/keyboard/resources/right-keys/UpArrow.png -------------------------------------------------------------------------------- /src-tauri/assets/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 | "Name": "默认喵", 14 | "File": "live2d_expression0.exp3.json" 15 | }, 16 | { 17 | "Name": "社会喵", 18 | "File": "live2d_expression1.exp3.json", 19 | "Description": "喵喵我叼根小烟,耍个帅气俏皮的wink~超有范儿!但直播里用这招,怕是会让铲屎官和平台瞪大眼,本喵还是低调点,偷偷耍酷好啦!" 20 | 21 | }, 22 | { 23 | "Name": "天使喵", 24 | "File": "live2d_expression2.exp3.json" 25 | } 26 | ], 27 | "Motions": { 28 | "CAT_motion": [ 29 | { 30 | "Name": "雷霆喵", 31 | "File": "live2d_motion1.motion3.json", 32 | "Sound": "live2d_motion1.flac", 33 | "FadeInTime": 0, 34 | "FadeOutTime": 0 35 | }, 36 | { 37 | "Name": "摇摆喵", 38 | "File": "live2d_motion2.motion3.json", 39 | "FadeInTime": 0, 40 | "FadeOutTime": 0 41 | } 42 | ] 43 | } 44 | }, 45 | "Groups": [ 46 | { 47 | "Target": "Parameter", 48 | "Name": "EyeBlink", 49 | "Ids": [ 50 | "ParamEyeLOpen", 51 | "ParamEyeROpen" 52 | ] 53 | }, 54 | { 55 | "Target": "Parameter", 56 | "Name": "LipSync", 57 | "Ids": [] 58 | } 59 | ], 60 | "HitAreas": [] 61 | } 62 | -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/demomodel.1024/texture_00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/demomodel.1024/texture_00.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/demomodel.1024/texture_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/demomodel.1024/texture_01.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/demomodel.1024/texture_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/demomodel.1024/texture_02.png -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/demomodel.moc3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/demomodel.moc3 -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/live2d_expression0.exp3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Type": "Live2D Expression", 3 | "Parameters": [] 4 | } 5 | -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/live2d_motion1.flac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/live2d_motion1.flac -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/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 | -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/background.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/cover.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Alt.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/AltGr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/AltGr.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/BackQuote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/BackQuote.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Backspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Backspace.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/CapsLock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/CapsLock.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Control.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/ControlLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/ControlLeft.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/ControlRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/ControlRight.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Delete.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Escape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Escape.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Fn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Fn.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyA.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyB.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyC.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyD.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyE.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyF.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyG.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyH.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyI.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyJ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyJ.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyK.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyL.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyM.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyN.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyO.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyP.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyQ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyQ.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyR.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyS.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyT.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyU.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyU.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyV.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyV.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyW.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyX.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyY.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/KeyZ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/KeyZ.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Meta.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Num0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Num0.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Num1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Num1.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Num2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Num2.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Num3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Num3.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Num4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Num4.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Num5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Num5.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Num6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Num6.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Num7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Num7.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Num8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Num8.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Num9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Num9.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Return.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Return.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Shift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Shift.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/ShiftLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/ShiftLeft.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/ShiftRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/ShiftRight.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Slash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Slash.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Space.png -------------------------------------------------------------------------------- /src-tauri/assets/models/standard/resources/left-keys/Tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/src-tauri/assets/models/standard/resources/left-keys/Tab.png -------------------------------------------------------------------------------- /src-tauri/assets/tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayangweb/BongoCat/0825e5ffa54cbad13888693dffbe96063e1a6f96/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 | { 22 | "identifier": "opener:allow-open-path", 23 | "allow": [ 24 | { 25 | "path": "**/*" 26 | } 27 | ] 28 | }, 29 | "pinia:default", 30 | "log:default", 31 | "updater:default", 32 | "prevent-default:default", 33 | "autostart:default", 34 | "macos-permissions:default", 35 | "dialog:default", 36 | "fs:default", 37 | "fs:read-all", 38 | "fs:write-all", 39 | { 40 | "identifier": "fs:scope", 41 | "allow": [ 42 | "**/*" 43 | ] 44 | }, 45 | "clipboard-manager:allow-write-text" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /src-tauri/src/core/device.rs: -------------------------------------------------------------------------------- 1 | use rdev::{Event, EventType, listen}; 2 | use serde::Serialize; 3 | use serde_json::{Value, json}; 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::{AppHandle, Emitter, EventTarget, WebviewWindow}; 2 | use tauri_nspanel::{ 3 | WebviewWindowExt, 4 | cocoa::appkit::{NSMainMenuWindowLevel, NSWindowCollectionBehavior}, 5 | panel_delegate, 6 | }; 7 | use tauri_plugin_custom_window::MAIN_WINDOW_LABEL; 8 | 9 | #[allow(non_upper_case_globals)] 10 | const NSWindowStyleMaskNonActivatingPanel: i32 = 1 << 7; 11 | #[allow(non_upper_case_globals)] 12 | const NSResizableWindowMask: i32 = 1 << 3; 13 | const WINDOW_FOCUS_EVENT: &str = "tauri://focus"; 14 | const WINDOW_BLUR_EVENT: &str = "tauri://blur"; 15 | const WINDOW_MOVED_EVENT: &str = "tauri://move"; 16 | const WINDOW_RESIZED_EVENT: &str = "tauri://resize"; 17 | 18 | pub fn platform( 19 | app_handle: &AppHandle, 20 | main_window: WebviewWindow, 21 | _preference_window: WebviewWindow, 22 | ) { 23 | let _ = app_handle.plugin(tauri_nspanel::init()); 24 | 25 | let _ = app_handle.set_dock_visibility(false); 26 | 27 | let panel = main_window.to_panel().unwrap(); 28 | 29 | panel.set_level(NSMainMenuWindowLevel); 30 | 31 | panel.set_style_mask(NSWindowStyleMaskNonActivatingPanel | NSResizableWindowMask); 32 | 33 | panel.set_collection_behaviour( 34 | NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces 35 | | NSWindowCollectionBehavior::NSWindowCollectionBehaviorStationary 36 | | NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenAuxiliary, 37 | ); 38 | 39 | let delegate = panel_delegate!(EcoPanelDelegate { 40 | window_did_become_key, 41 | window_did_resign_key, 42 | window_did_resize, 43 | window_did_move 44 | }); 45 | 46 | delegate.set_listener(Box::new(move |delegate_name: String| { 47 | let target = EventTarget::labeled(MAIN_WINDOW_LABEL); 48 | 49 | let window_move_event = || { 50 | if let Ok(position) = main_window.outer_position() { 51 | let _ = main_window.emit_to(target.clone(), WINDOW_MOVED_EVENT, position); 52 | } 53 | }; 54 | 55 | match delegate_name.as_str() { 56 | "window_did_become_key" => { 57 | let _ = main_window.emit_to(target, WINDOW_FOCUS_EVENT, true); 58 | } 59 | "window_did_resign_key" => { 60 | let _ = main_window.emit_to(target, WINDOW_BLUR_EVENT, true); 61 | } 62 | "window_did_resize" => { 63 | window_move_event(); 64 | 65 | if let Ok(size) = main_window.inner_size() { 66 | let _ = main_window.emit_to(target, WINDOW_RESIZED_EVENT, size); 67 | } 68 | } 69 | "window_did_move" => window_move_event(), 70 | _ => (), 71 | } 72 | })); 73 | 74 | panel.set_delegate(delegate); 75 | } 76 | -------------------------------------------------------------------------------- /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 | mod utils; 3 | 4 | use core::{device, prevent_default, setup}; 5 | use tauri::{Manager, WindowEvent, generate_handler}; 6 | use tauri_plugin_autostart::MacosLauncher; 7 | use tauri_plugin_custom_window::{ 8 | MAIN_WINDOW_LABEL, PREFERENCE_WINDOW_LABEL, show_preference_window, 9 | }; 10 | use utils::fs_extra::copy_dir; 11 | 12 | #[cfg_attr(mobile, tauri::mobile_entry_point)] 13 | pub fn run() { 14 | let app = tauri::Builder::default() 15 | .setup(|app| { 16 | let app_handle = app.handle(); 17 | 18 | let main_window = app.get_webview_window(MAIN_WINDOW_LABEL).unwrap(); 19 | 20 | let preference_window = app.get_webview_window(PREFERENCE_WINDOW_LABEL).unwrap(); 21 | 22 | setup::default(&app_handle, main_window.clone(), preference_window.clone()); 23 | 24 | device::start_listening(app_handle.clone()); 25 | 26 | Ok(()) 27 | }) 28 | .invoke_handler(generate_handler![copy_dir]) 29 | .plugin(tauri_plugin_custom_window::init()) 30 | .plugin(tauri_plugin_os::init()) 31 | .plugin(tauri_plugin_process::init()) 32 | .plugin(tauri_plugin_opener::init()) 33 | .plugin(tauri_plugin_pinia::init()) 34 | .plugin(tauri_plugin_updater::Builder::new().build()) 35 | .plugin(prevent_default::init()) 36 | .plugin(tauri_plugin_single_instance::init( 37 | |app_handle, _argv, _cwd| { 38 | show_preference_window(app_handle); 39 | }, 40 | )) 41 | .plugin(tauri_plugin_log::Builder::new().build()) 42 | .plugin(tauri_plugin_autostart::init( 43 | MacosLauncher::LaunchAgent, 44 | None, 45 | )) 46 | .plugin(tauri_plugin_macos_permissions::init()) 47 | .plugin(tauri_plugin_dialog::init()) 48 | .plugin(tauri_plugin_fs::init()) 49 | .plugin(tauri_plugin_clipboard_manager::init()) 50 | .on_window_event(|window, event| match event { 51 | WindowEvent::CloseRequested { api, .. } => { 52 | let _ = window.hide(); 53 | 54 | api.prevent_close(); 55 | } 56 | _ => {} 57 | }) 58 | .build(tauri::generate_context!()) 59 | .expect("error while running tauri application"); 60 | 61 | app.run(|app_handle, event| match event { 62 | #[cfg(target_os = "macos")] 63 | tauri::RunEvent::Reopen { .. } => { 64 | show_preference_window(app_handle); 65 | } 66 | _ => { 67 | let _ = app_handle; 68 | } 69 | }); 70 | } 71 | -------------------------------------------------------------------------------- /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 = "2024" 7 | rust-version = "1.85.0" 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", "set_always_on_top"]; 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", "allow-set-always-on-top"] 6 | -------------------------------------------------------------------------------- /src-tauri/src/plugins/window/src/commands/macos.rs: -------------------------------------------------------------------------------- 1 | use super::{is_main_window, shared_hide_window, shared_set_always_on_top, shared_show_window}; 2 | use crate::MAIN_WINDOW_LABEL; 3 | use tauri::{AppHandle, Runtime, WebviewWindow, command}; 4 | use tauri_nspanel::{ManagerExt, cocoa::appkit::NSMainMenuWindowLevel}; 5 | 6 | pub enum MacOSPanelStatus { 7 | Show, 8 | Hide, 9 | SetAlwaysOnTop(bool), 10 | } 11 | 12 | #[command] 13 | pub async fn show_window(app_handle: AppHandle, window: WebviewWindow) { 14 | if is_main_window(&window) { 15 | set_macos_panel(&app_handle, &window, MacOSPanelStatus::Show); 16 | } else { 17 | shared_show_window(&app_handle, &window); 18 | } 19 | } 20 | 21 | #[command] 22 | pub async fn hide_window(app_handle: AppHandle, window: WebviewWindow) { 23 | if is_main_window(&window) { 24 | set_macos_panel(&app_handle, &window, MacOSPanelStatus::Hide); 25 | } else { 26 | shared_hide_window(&app_handle, &window); 27 | } 28 | } 29 | 30 | #[command] 31 | pub async fn set_always_on_top( 32 | app_handle: AppHandle, 33 | window: WebviewWindow, 34 | always_on_top: bool, 35 | ) { 36 | if is_main_window(&window) { 37 | set_macos_panel( 38 | &app_handle, 39 | &window, 40 | MacOSPanelStatus::SetAlwaysOnTop(always_on_top), 41 | ); 42 | } else { 43 | shared_set_always_on_top(&app_handle, &window, always_on_top); 44 | } 45 | } 46 | 47 | pub fn set_macos_panel( 48 | app_handle: &AppHandle, 49 | window: &WebviewWindow, 50 | status: MacOSPanelStatus, 51 | ) { 52 | if is_main_window(window) { 53 | let app_handle_clone = app_handle.clone(); 54 | 55 | let _ = app_handle.run_on_main_thread(move || { 56 | if let Ok(panel) = app_handle_clone.get_webview_panel(MAIN_WINDOW_LABEL) { 57 | match status { 58 | MacOSPanelStatus::Show => { 59 | panel.show(); 60 | } 61 | MacOSPanelStatus::Hide => { 62 | panel.order_out(None); 63 | } 64 | MacOSPanelStatus::SetAlwaysOnTop(always_on_top) => { 65 | if always_on_top { 66 | panel.set_level(NSMainMenuWindowLevel); 67 | } else { 68 | panel.set_level(-1); 69 | }; 70 | } 71 | } 72 | } 73 | }); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src-tauri/src/plugins/window/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | use tauri::{AppHandle, Manager, Runtime, WebviewWindow, async_runtime::spawn}; 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 | 28 | fn shared_hide_window(_app_handle: &AppHandle, window: &WebviewWindow) { 29 | let _ = window.hide(); 30 | } 31 | 32 | fn shared_set_always_on_top( 33 | _app_handle: &AppHandle, 34 | window: &WebviewWindow, 35 | always_on_top: bool, 36 | ) { 37 | if always_on_top { 38 | let _ = window.set_always_on_bottom(false); 39 | let _ = window.set_always_on_top(true); 40 | } else { 41 | let _ = window.set_always_on_top(false); 42 | let _ = window.set_always_on_bottom(true); 43 | } 44 | } 45 | 46 | pub fn show_main_window(app_handle: &AppHandle) { 47 | show_window_by_label(app_handle, MAIN_WINDOW_LABEL); 48 | } 49 | 50 | pub fn show_preference_window(app_handle: &AppHandle) { 51 | show_window_by_label(app_handle, PREFERENCE_WINDOW_LABEL); 52 | } 53 | 54 | fn show_window_by_label(app_handle: &AppHandle, label: &str) { 55 | if let Some(window) = app_handle.get_webview_window(label) { 56 | let app_handle_clone = app_handle.clone(); 57 | 58 | spawn(async move { 59 | show_window(app_handle_clone, window).await; 60 | }); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src-tauri/src/plugins/window/src/commands/not_macos.rs: -------------------------------------------------------------------------------- 1 | use super::{shared_hide_window, shared_set_always_on_top, shared_show_window}; 2 | use tauri::{AppHandle, Runtime, WebviewWindow, command}; 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 | 14 | #[command] 15 | pub async fn set_always_on_top( 16 | app_handle: AppHandle, 17 | window: WebviewWindow, 18 | always_on_top: bool, 19 | ) { 20 | shared_set_always_on_top(&app_handle, &window, always_on_top); 21 | } 22 | -------------------------------------------------------------------------------- /src-tauri/src/plugins/window/src/lib.rs: -------------------------------------------------------------------------------- 1 | use tauri::{ 2 | Runtime, generate_handler, 3 | plugin::{Builder, TauriPlugin}, 4 | }; 5 | 6 | mod commands; 7 | 8 | pub use commands::*; 9 | 10 | pub fn init() -> TauriPlugin { 11 | Builder::new("custom-window") 12 | .invoke_handler(generate_handler![ 13 | commands::show_window, 14 | commands::hide_window, 15 | commands::set_always_on_top 16 | ]) 17 | .build() 18 | } 19 | -------------------------------------------------------------------------------- /src-tauri/src/utils/fs_extra.rs: -------------------------------------------------------------------------------- 1 | use fs_extra::dir::{CopyOptions, copy}; 2 | use std::fs::create_dir_all; 3 | use tauri::command; 4 | 5 | #[command] 6 | pub async fn copy_dir(from_path: String, to_path: String) -> Result<(), String> { 7 | let mut options = CopyOptions::new(); 8 | options.content_only = true; 9 | 10 | create_dir_all(&to_path).map_err(|err| err.to_string())?; 11 | 12 | copy(from_path, to_path, &options).map_err(|err| err.to_string())?; 13 | 14 | Ok(()) 15 | } 16 | -------------------------------------------------------------------------------- /src-tauri/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod fs_extra; 2 | -------------------------------------------------------------------------------- /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 | "shadow": false, 20 | "alwaysOnTop": true, 21 | "transparent": true, 22 | "decorations": false, 23 | "acceptFirstMouse": true, 24 | "skipTaskbar": true 25 | }, 26 | { 27 | "label": "preference", 28 | "title": "偏好设置", 29 | "url": "index.html/#/preference", 30 | "visible": false, 31 | "titleBarStyle": "Overlay", 32 | "hiddenTitle": true 33 | } 34 | ], 35 | "security": { 36 | "csp": null, 37 | "dangerousDisableAssetCspModification": true, 38 | "assetProtocol": { 39 | "enable": true, 40 | "scope": { 41 | "allow": ["**/*"], 42 | "requireLiteralLeadingDot": false 43 | } 44 | } 45 | } 46 | }, 47 | "bundle": { 48 | "active": true, 49 | "category": "Game", 50 | "createUpdaterArtifacts": true, 51 | "targets": ["nsis", "dmg", "app", "appimage", "deb", "rpm"], 52 | "shortDescription": "BongoCat", 53 | "icon": [ 54 | "icons/32x32.png", 55 | "icons/128x128.png", 56 | "icons/128x128@2x.png", 57 | "icons/icon.icns", 58 | "icons/icon.ico" 59 | ], 60 | "resources": ["assets/tray.png", "assets/models"] 61 | }, 62 | "plugins": { 63 | "updater": { 64 | "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEVBRjJFMzE3MjEwMUZEMTAKUldRUS9RRWhGK1B5NmdkemhKcUFrVjZBQXlzdExpakdWVEJDeU9XckVsbzV2cFIycVJOempWa2UK", 65 | "endpoints": [ 66 | "https://gh-proxy.com/github.com/ayangweb/BongoCat/releases/latest/download/latest.json" 67 | ] 68 | }, 69 | "fs": { 70 | "requireLiteralLeadingDot": false 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /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/models"] 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 | 76 | 77 | 82 | -------------------------------------------------------------------------------- /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 | .ant-card { 19 | .ant-card-actions { 20 | > li { 21 | --uno: flex items-center justify-center; 22 | > span { 23 | --uno: inline-flex items-center justify-center min-w-unset; 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/components/pro-list-item/index.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 60 | -------------------------------------------------------------------------------- /src/components/pro-list/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 30 | -------------------------------------------------------------------------------- /src/components/update-app/index.vue: -------------------------------------------------------------------------------- 1 | 129 | 130 | 177 | 178 | 185 | -------------------------------------------------------------------------------- /src/composables/useDevice.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue' 2 | 3 | import { readDir } from '@tauri-apps/plugin-fs' 4 | import { uniq } from 'es-toolkit' 5 | import { reactive, ref, watch } from 'vue' 6 | 7 | import { LISTEN_KEY } from '../constants' 8 | 9 | import { useTauriListen } from './useTauriListen' 10 | 11 | import { useCatStore } from '@/stores/cat' 12 | import { useModelStore } from '@/stores/model' 13 | import { isImage } from '@/utils/is' 14 | import { join } from '@/utils/path' 15 | import { isWindows } from '@/utils/platform' 16 | 17 | interface MouseButtonEvent { 18 | kind: 'MousePress' | 'MouseRelease' 19 | value: string 20 | } 21 | 22 | interface MouseMoveValue { 23 | x: number 24 | y: number 25 | } 26 | 27 | interface MouseMoveEvent { 28 | kind: 'MouseMove' 29 | value: MouseMoveValue 30 | } 31 | 32 | interface KeyboardEvent { 33 | kind: 'KeyboardPress' | 'KeyboardRelease' 34 | value: string 35 | } 36 | 37 | type DeviceEvent = MouseButtonEvent | MouseMoveEvent | KeyboardEvent 38 | 39 | export function useDevice() { 40 | const supportLeftKeys = ref([]) 41 | const supportRightKeys = ref([]) 42 | const pressedMouses = ref([]) 43 | const mousePosition = reactive({ x: 0, y: 0 }) 44 | const pressedLeftKeys = ref([]) 45 | const pressedRightKeys = ref([]) 46 | const catStore = useCatStore() 47 | const modelStore = useModelStore() 48 | const releaseTimers = new Map() 49 | 50 | watch(() => modelStore.currentModel, async (model) => { 51 | if (!model) return 52 | 53 | const keySides = [ 54 | { 55 | side: 'left', 56 | supportKeys: supportLeftKeys, 57 | pressedKeys: pressedLeftKeys, 58 | }, 59 | { 60 | side: 'right', 61 | supportKeys: supportRightKeys, 62 | pressedKeys: pressedRightKeys, 63 | }, 64 | ] 65 | 66 | for await (const item of keySides) { 67 | const { side, supportKeys, pressedKeys } = item 68 | 69 | try { 70 | const files = await readDir(join(model.path, 'resources', `${side}-keys`)) 71 | 72 | const imageFiles = files.filter(file => isImage(file.name)) 73 | 74 | supportKeys.value = imageFiles.map((item) => { 75 | return item.name.split('.')[0] 76 | }) 77 | 78 | pressedKeys.value = pressedKeys.value.filter((key) => { 79 | return supportKeys.value.includes(key) 80 | }) 81 | } catch { 82 | supportKeys.value = [] 83 | pressedKeys.value = [] 84 | } 85 | } 86 | }, { deep: true, immediate: true }) 87 | 88 | const handlePress = (array: Ref, value?: string) => { 89 | if (!value) return 90 | 91 | if (catStore.singleMode) { 92 | array.value = [value] 93 | } else { 94 | array.value = uniq(array.value.concat(value)) 95 | } 96 | } 97 | 98 | const handleRelease = (array: Ref, value?: string) => { 99 | if (!value) return 100 | 101 | array.value = array.value.filter(item => item !== value) 102 | } 103 | 104 | const getSupportedKey = (key: string) => { 105 | for (const side of ['left', 'right']) { 106 | let nextKey = key 107 | 108 | const supportKeys = side === 'left' ? supportLeftKeys.value : supportRightKeys.value 109 | 110 | const unsupportedKeys = !supportKeys.includes(key) 111 | 112 | if (key.startsWith('F') && unsupportedKeys) { 113 | nextKey = key.replace(/F(\d+)/, 'Fn') 114 | } 115 | 116 | for (const item of ['Meta', 'Shift', 'Alt', 'Control']) { 117 | if (key.startsWith(item) && unsupportedKeys) { 118 | const regex = new RegExp(`^(${item}).*`) 119 | nextKey = key.replace(regex, '$1') 120 | } 121 | } 122 | 123 | if (!supportKeys.includes(nextKey)) continue 124 | 125 | return nextKey 126 | } 127 | } 128 | 129 | const handleScheduleRelease = (keys: Ref, key: string, delay = 500) => { 130 | if (releaseTimers.has(key)) { 131 | clearTimeout(releaseTimers.get(key)) 132 | } 133 | 134 | const timer = setTimeout(() => { 135 | handleRelease(keys, key) 136 | 137 | releaseTimers.delete(key) 138 | }, delay) 139 | 140 | releaseTimers.set(key, timer) 141 | } 142 | 143 | useTauriListen(LISTEN_KEY.DEVICE_CHANGED, ({ payload }) => { 144 | const { kind, value } = payload 145 | 146 | if (kind === 'KeyboardPress' || kind === 'KeyboardRelease') { 147 | const nextValue = getSupportedKey(value) 148 | 149 | if (!nextValue) return 150 | 151 | const isLeftSide = supportLeftKeys.value.includes(nextValue) 152 | 153 | const pressedKeys = isLeftSide ? pressedLeftKeys : pressedRightKeys 154 | 155 | if (nextValue === 'CapsLock') { 156 | handlePress(pressedKeys, nextValue) 157 | 158 | return handleScheduleRelease(pressedKeys, nextValue, 100) 159 | } 160 | 161 | if (kind === 'KeyboardPress') { 162 | if (isWindows) { 163 | handleScheduleRelease(pressedKeys, nextValue) 164 | } 165 | 166 | return handlePress(pressedKeys, nextValue) 167 | } 168 | 169 | return handleRelease(pressedKeys, nextValue) 170 | } 171 | 172 | switch (kind) { 173 | case 'MousePress': 174 | return handlePress(pressedMouses, value) 175 | case 'MouseRelease': 176 | return handleRelease(pressedMouses, value) 177 | case 'MouseMove': 178 | return Object.assign(mousePosition, value) 179 | } 180 | }) 181 | 182 | return { 183 | pressedMouses, 184 | mousePosition, 185 | pressedLeftKeys, 186 | pressedRightKeys, 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/composables/useModel.ts: -------------------------------------------------------------------------------- 1 | import { convertFileSrc } from '@tauri-apps/api/core' 2 | import { LogicalSize, PhysicalSize } from '@tauri-apps/api/dpi' 3 | import { resolveResource } from '@tauri-apps/api/path' 4 | import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow' 5 | import { message } from 'ant-design-vue' 6 | import { isNil, round } from 'es-toolkit' 7 | import { computed, watch } from 'vue' 8 | 9 | import live2d from '../utils/live2d' 10 | import { getCursorMonitor } from '../utils/monitor' 11 | 12 | import { useCatStore } from '@/stores/cat' 13 | import { useModelStore } from '@/stores/model' 14 | import { getImageSize } from '@/utils/dom' 15 | import { join } from '@/utils/path' 16 | 17 | const appWindow = getCurrentWebviewWindow() 18 | 19 | export function useModel() { 20 | const modelStore = useModelStore() 21 | const catStore = useCatStore() 22 | 23 | const backgroundImage = computed(() => { 24 | return convertFileSrc(join(modelStore.currentModel!.path, 'resources', 'background.png')) 25 | }) 26 | 27 | watch(() => modelStore.currentModel, handleLoad, { deep: true, immediate: true }) 28 | 29 | watch(() => catStore.scale, async () => { 30 | const { width, height } = await getImageSize(backgroundImage.value) 31 | 32 | appWindow.setSize( 33 | new PhysicalSize({ 34 | width: round(width * (catStore.scale / 100)), 35 | height: round(height * (catStore.scale / 100)), 36 | }), 37 | ) 38 | }, { immediate: true }) 39 | 40 | async function handleLoad() { 41 | try { 42 | const { path } = modelStore.currentModel! 43 | 44 | await resolveResource(path) 45 | 46 | const data = await live2d.load(path) 47 | 48 | handleResize() 49 | 50 | Object.assign(modelStore, data) 51 | } catch (error) { 52 | message.error(String(error)) 53 | } 54 | } 55 | 56 | function handleDestroy() { 57 | live2d.destroy() 58 | } 59 | 60 | async function handleResize() { 61 | if (!live2d.model) return 62 | 63 | const { innerWidth, innerHeight } = window 64 | 65 | const { width, height } = await getImageSize(backgroundImage.value) 66 | 67 | live2d.model?.scale.set(innerWidth / width) 68 | 69 | if (round(innerWidth / innerHeight, 1) !== round(width / height, 1)) { 70 | await appWindow.setSize( 71 | new LogicalSize({ 72 | width: innerWidth, 73 | height: Math.ceil(innerWidth * (height / width)), 74 | }), 75 | ) 76 | } 77 | 78 | const size = await appWindow.size() 79 | 80 | catStore.scale = round((size.width / width) * 100) 81 | } 82 | 83 | function handleKeyDown(side: 'left' | 'right', pressed: boolean) { 84 | const id = side === 'left' ? 'CatParamLeftHandDown' : 'CatParamRightHandDown' 85 | 86 | const { min, max } = live2d.getParameterRange(id) 87 | 88 | live2d.setParameterValue(id, pressed ? max : min) 89 | } 90 | 91 | async function handleMouseMove() { 92 | const monitor = await getCursorMonitor() 93 | 94 | if (!monitor) return 95 | 96 | const { size, position, cursorPosition } = monitor 97 | 98 | const xRatio = (cursorPosition.x - position.x) / size.width 99 | const yRatio = (cursorPosition.y - position.y) / size.height 100 | 101 | for (const id of ['ParamMouseX', 'ParamMouseY', 'ParamAngleX', 'ParamAngleY']) { 102 | const { min, max } = live2d.getParameterRange(id) 103 | 104 | if (isNil(min) || isNil(max)) continue 105 | 106 | const isXAxis = id.endsWith('X') 107 | 108 | const ratio = isXAxis ? xRatio : yRatio 109 | let value = max - (ratio * (max - min)) 110 | 111 | if (isXAxis && catStore.mouseMirror) { 112 | value *= -1 113 | } 114 | 115 | live2d.setParameterValue(id, value) 116 | } 117 | } 118 | 119 | function handleMouseDown(value: string[]) { 120 | const params = { 121 | ParamMouseLeftDown: value.includes('Left'), 122 | ParamMouseRightDown: value.includes('Right'), 123 | } 124 | 125 | for (const [id, pressed] of Object.entries(params)) { 126 | const { min, max } = live2d.getParameterRange(id) 127 | 128 | live2d.setParameterValue(id, pressed ? max : min) 129 | } 130 | } 131 | 132 | return { 133 | backgroundImage, 134 | handleLoad, 135 | handleDestroy, 136 | handleResize, 137 | handleKeyDown, 138 | handleMouseMove, 139 | handleMouseDown, 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/composables/useSharedMenu.ts: -------------------------------------------------------------------------------- 1 | import { CheckMenuItem, MenuItem, PredefinedMenuItem, Submenu } from '@tauri-apps/api/menu' 2 | import { range } from 'es-toolkit' 3 | 4 | import { hideWindow, showWindow } from '@/plugins/window' 5 | import { useCatStore } from '@/stores/cat' 6 | import { isMac } from '@/utils/platform' 7 | 8 | export function useSharedMenu() { 9 | const catStore = useCatStore() 10 | 11 | const getScaleMenuItems = async () => { 12 | const options = range(50, 151, 25) 13 | 14 | const items = options.map((item) => { 15 | return CheckMenuItem.new({ 16 | text: item === 100 ? '默认' : `${item}%`, 17 | checked: catStore.scale === item, 18 | action: () => { 19 | catStore.scale = item 20 | }, 21 | }) 22 | }) 23 | 24 | if (!options.includes(catStore.scale)) { 25 | items.unshift(CheckMenuItem.new({ 26 | text: `${catStore.scale}%`, 27 | checked: true, 28 | enabled: false, 29 | })) 30 | } 31 | 32 | return Promise.all(items) 33 | } 34 | 35 | const getOpacityMenuItems = async () => { 36 | const options = range(25, 101, 25) 37 | 38 | const items = options.map((item) => { 39 | return CheckMenuItem.new({ 40 | text: `${item}%`, 41 | checked: catStore.opacity === item, 42 | action: () => { 43 | catStore.opacity = item 44 | }, 45 | }) 46 | }) 47 | 48 | if (!options.includes(catStore.opacity)) { 49 | items.unshift(CheckMenuItem.new({ 50 | text: `${catStore.opacity}%`, 51 | checked: true, 52 | enabled: false, 53 | })) 54 | } 55 | 56 | return Promise.all(items) 57 | } 58 | 59 | const getSharedMenu = async () => { 60 | return await Promise.all([ 61 | MenuItem.new({ 62 | text: '偏好设置...', 63 | accelerator: isMac ? 'Cmd+,' : '', 64 | action: () => showWindow('preference'), 65 | }), 66 | MenuItem.new({ 67 | text: catStore.visible ? '隐藏猫咪' : '显示猫咪', 68 | action: () => { 69 | if (catStore.visible) { 70 | hideWindow('main') 71 | } else { 72 | showWindow('main') 73 | } 74 | 75 | catStore.visible = !catStore.visible 76 | }, 77 | }), 78 | PredefinedMenuItem.new({ item: 'Separator' }), 79 | CheckMenuItem.new({ 80 | text: '窗口穿透', 81 | checked: catStore.penetrable, 82 | action: () => { 83 | catStore.penetrable = !catStore.penetrable 84 | }, 85 | }), 86 | Submenu.new({ 87 | text: '窗口尺寸', 88 | items: await getScaleMenuItems(), 89 | }), 90 | Submenu.new({ 91 | text: '不透明度', 92 | items: await getOpacityMenuItems(), 93 | }), 94 | ]) 95 | } 96 | 97 | return { 98 | getSharedMenu, 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /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 { kebabCase } from 'es-toolkit' 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-${kebabCase(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 { Menu, MenuItem, PredefinedMenuItem } 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 { watchDebounced } from '@vueuse/core' 11 | 12 | import { GITHUB_LINK, LISTEN_KEY } from '../constants' 13 | import { showWindow } from '../plugins/window' 14 | import { isMac } from '../utils/platform' 15 | 16 | import { useSharedMenu } from './useSharedMenu' 17 | 18 | import { useCatStore } from '@/stores/cat' 19 | 20 | const TRAY_ID = 'BONGO_CAT_TRAY' 21 | 22 | export function useTray() { 23 | const catStore = useCatStore() 24 | const { getSharedMenu } = useSharedMenu() 25 | 26 | watchDebounced(() => catStore, () => { 27 | updateTrayMenu() 28 | }, { deep: true, debounce: 500 }) 29 | 30 | const createTray = async () => { 31 | const tray = await getTrayById() 32 | 33 | if (tray) return 34 | 35 | const appName = await getName() 36 | const appVersion = await getVersion() 37 | 38 | const menu = await getTrayMenu() 39 | 40 | const icon = await resolveResource('assets/tray.png') 41 | 42 | const options: TrayIconOptions = { 43 | menu, 44 | icon, 45 | id: TRAY_ID, 46 | tooltip: `${appName} v${appVersion}`, 47 | iconAsTemplate: false, 48 | menuOnLeftClick: true, 49 | } 50 | 51 | return TrayIcon.new(options) 52 | } 53 | 54 | const getTrayById = () => { 55 | return TrayIcon.getById(TRAY_ID) 56 | } 57 | 58 | const getTrayMenu = async () => { 59 | const appVersion = await getVersion() 60 | 61 | const items = await Promise.all([ 62 | ...await getSharedMenu(), 63 | PredefinedMenuItem.new({ item: 'Separator' }), 64 | MenuItem.new({ 65 | text: '检查更新', 66 | action: () => { 67 | showWindow() 68 | 69 | emit(LISTEN_KEY.UPDATE_APP) 70 | }, 71 | }), 72 | MenuItem.new({ 73 | text: '开源地址', 74 | action: () => openUrl(GITHUB_LINK), 75 | }), 76 | PredefinedMenuItem.new({ item: 'Separator' }), 77 | MenuItem.new({ 78 | text: `版本 ${appVersion}`, 79 | enabled: false, 80 | }), 81 | MenuItem.new({ 82 | text: '重启应用', 83 | action: relaunch, 84 | }), 85 | MenuItem.new({ 86 | text: '退出应用', 87 | accelerator: isMac ? 'Cmd+Q' : '', 88 | action: () => exit(0), 89 | }), 90 | ]) 91 | 92 | return Menu.new({ items }) 93 | } 94 | 95 | const updateTrayMenu = async () => { 96 | const tray = await getTrayById() 97 | 98 | if (!tray) return 99 | 100 | const menu = await getTrayMenu() 101 | 102 | tray.setMenu(menu) 103 | } 104 | 105 | return { 106 | createTray, 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /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 { availableMonitors } from '@tauri-apps/api/window' 6 | import { isNumber } from 'es-toolkit/compat' 7 | import { onMounted, ref } from 'vue' 8 | 9 | import { useAppStore } from '@/stores/app' 10 | 11 | export type WindowState = Record | undefined> 12 | 13 | const appWindow = getCurrentWebviewWindow() 14 | const { label } = appWindow 15 | 16 | export function useWindowState() { 17 | const appStore = useAppStore() 18 | const isRestored = ref(false) 19 | 20 | onMounted(() => { 21 | appWindow.onMoved(onChange) 22 | 23 | appWindow.onResized(onChange) 24 | }) 25 | 26 | const onChange = async (event: Event) => { 27 | const minimized = await appWindow.isMinimized() 28 | 29 | if (minimized) return 30 | 31 | appStore.windowState[label] ??= {} 32 | 33 | Object.assign(appStore.windowState[label], event.payload) 34 | } 35 | 36 | const restoreState = async () => { 37 | const { x, y, width, height } = appStore.windowState[label] ?? {} 38 | 39 | if (isNumber(x) && isNumber(y)) { 40 | const monitors = await availableMonitors() 41 | 42 | const monitor = monitors.find((monitor) => { 43 | const { position, size } = monitor 44 | 45 | const inBoundsX = x >= position.x && x <= position.x + size.width 46 | const inBoundsY = y >= position.y && y <= position.y + size.height 47 | 48 | return inBoundsX && inBoundsY 49 | }) 50 | 51 | if (monitor) { 52 | await appWindow.setPosition(new PhysicalPosition(x, y)) 53 | } 54 | } 55 | 56 | if (width && height) { 57 | await appWindow.setSize(new PhysicalSize(width, height)) 58 | } 59 | 60 | isRestored.value = true 61 | } 62 | 63 | return { 64 | isRestored, 65 | restoreState, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /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 | 10 | export const INVOKE_KEY = { 11 | COPY_DIR: 'copy_dir', 12 | } 13 | -------------------------------------------------------------------------------- /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/main/index.vue: -------------------------------------------------------------------------------- 1 | 75 | 76 | 110 | -------------------------------------------------------------------------------- /src/pages/preference/components/about/index.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 104 | -------------------------------------------------------------------------------- /src/pages/preference/components/cat/index.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 86 | -------------------------------------------------------------------------------- /src/pages/preference/components/general/components/macos-permissions/index.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 67 | -------------------------------------------------------------------------------- /src/pages/preference/components/general/index.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 42 | -------------------------------------------------------------------------------- /src/pages/preference/components/model/components/float-menu/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 36 | -------------------------------------------------------------------------------- /src/pages/preference/components/model/components/upload/index.vue: -------------------------------------------------------------------------------- 1 | 85 | 86 | 98 | -------------------------------------------------------------------------------- /src/pages/preference/components/model/index.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 95 | -------------------------------------------------------------------------------- /src/pages/preference/index.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 96 | -------------------------------------------------------------------------------- /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 | SET_ALWAYS_ON_TOP: 'plugin:custom-window|set_always_on_top', 12 | } 13 | 14 | export function showWindow(label?: WindowLabel) { 15 | if (label) { 16 | emit(LISTEN_KEY.SHOW_WINDOW, label) 17 | } else { 18 | invoke(COMMAND.SHOW_WINDOW) 19 | } 20 | } 21 | 22 | export function hideWindow(label?: WindowLabel) { 23 | if (label) { 24 | emit(LISTEN_KEY.HIDE_WINDOW, label) 25 | } else { 26 | invoke(COMMAND.HIDE_WINDOW) 27 | } 28 | } 29 | 30 | export function setAlwaysOnTop(alwaysOnTop: boolean) { 31 | invoke(COMMAND.SET_ALWAYS_ON_TOP, { alwaysOnTop }) 32 | } 33 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import type { RouteRecordRaw } from 'vue-router' 2 | 3 | import { createRouter, createWebHashHistory } from 'vue-router' 4 | 5 | import Main from '../pages/main/index.vue' 6 | import Preference from '../pages/preference/index.vue' 7 | 8 | const routes: Readonly = [ 9 | { 10 | path: '/', 11 | component: Main, 12 | }, 13 | { 14 | path: '/preference', 15 | component: Preference, 16 | }, 17 | ] 18 | 19 | const router = createRouter({ 20 | history: createWebHashHistory(), 21 | routes, 22 | }) 23 | 24 | export default router 25 | -------------------------------------------------------------------------------- /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 visible = ref(true) 6 | const mirrorMode = ref(false) 7 | const singleMode = ref(false) 8 | const mouseMirror = ref(false) 9 | const penetrable = ref(false) 10 | const alwaysOnTop = ref(true) 11 | const scale = ref(100) 12 | const opacity = ref(100) 13 | 14 | return { 15 | visible, 16 | mirrorMode, 17 | singleMode, 18 | mouseMirror, 19 | penetrable, 20 | alwaysOnTop, 21 | scale, 22 | opacity, 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /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 | const autostart = ref(false) 7 | 8 | return { 9 | autoCheckUpdate, 10 | autostart, 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/stores/model.ts: -------------------------------------------------------------------------------- 1 | import { resolveResource } from '@tauri-apps/api/path' 2 | import { nanoid } from 'nanoid' 3 | import { defineStore } from 'pinia' 4 | import { onMounted, ref } from 'vue' 5 | 6 | import { join } from '@/utils/path' 7 | 8 | export type ModelMode = 'standard' | 'keyboard' | 'handle' 9 | 10 | export interface Model { 11 | id: string 12 | path: string 13 | mode: ModelMode 14 | isPreset: boolean 15 | } 16 | 17 | interface Motion { 18 | Name: string 19 | File: string 20 | Sound?: string 21 | FadeInTime: number 22 | FadeOutTime: number 23 | Description?: string 24 | } 25 | 26 | type MotionGroup = Record 27 | 28 | interface Expression { 29 | Name: string 30 | File: string 31 | Description?: string 32 | } 33 | 34 | export const useModelStore = defineStore('model', () => { 35 | const models = ref([]) 36 | const currentModel = ref() 37 | const motions = ref({}) 38 | const expressions = ref([]) 39 | 40 | onMounted(async () => { 41 | const modelsPath = await resolveResource('assets/models') 42 | 43 | if (models.value.length === 0) { 44 | const modes: ModelMode[] = ['standard', 'keyboard'] 45 | 46 | for await (const mode of modes) { 47 | const path = join(modelsPath, mode) 48 | 49 | models.value.push({ 50 | id: nanoid(), 51 | path, 52 | mode, 53 | isPreset: true, 54 | }) 55 | } 56 | } 57 | 58 | if (currentModel.value) return 59 | 60 | currentModel.value = models.value[0] 61 | }) 62 | 63 | return { 64 | models, 65 | currentModel, 66 | motions, 67 | expressions, 68 | } 69 | }) 70 | -------------------------------------------------------------------------------- /src/utils/dom.ts: -------------------------------------------------------------------------------- 1 | export function getImageSize(src: string) { 2 | return new Promise<{ width: number, height: number }>((resolve, reject) => { 3 | const img = new Image() 4 | 5 | img.src = src 6 | 7 | img.onload = () => { 8 | const { naturalWidth, naturalHeight } = img 9 | 10 | resolve({ width: naturalWidth, height: naturalHeight }) 11 | } 12 | 13 | img.onerror = reject 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/is.ts: -------------------------------------------------------------------------------- 1 | export function isImage(value: string) { 2 | const regex = /\.(?:jpe?g|png|webp|avif|gif|svg|bmp|ico|tiff?|heic|apng)$/i 3 | 4 | return regex.test(value) 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/live2d.ts: -------------------------------------------------------------------------------- 1 | import type { Cubism4InternalModel } from 'pixi-live2d-display' 2 | 3 | import { convertFileSrc } from '@tauri-apps/api/core' 4 | import { readDir, readTextFile } from '@tauri-apps/plugin-fs' 5 | import { Cubism4ModelSettings, Live2DModel } from 'pixi-live2d-display' 6 | import { Application, Ticker } from 'pixi.js' 7 | 8 | import { join } from './path' 9 | 10 | Live2DModel.registerTicker(Ticker) 11 | 12 | class Live2d { 13 | private app: Application | null = null 14 | public model: Live2DModel | null = null 15 | 16 | constructor() { } 17 | 18 | private mount() { 19 | const view = document.getElementById('live2dCanvas') as HTMLCanvasElement 20 | 21 | this.app = new Application({ 22 | view, 23 | resizeTo: window, 24 | backgroundAlpha: 0, 25 | autoDensity: true, 26 | resolution: devicePixelRatio, 27 | }) 28 | } 29 | 30 | public async load(path: string) { 31 | if (!this.app) { 32 | this.mount() 33 | } 34 | 35 | this.destroy() 36 | 37 | const files = await readDir(path) 38 | 39 | const modelFile = files.find(file => file.name.endsWith('.model3.json')) 40 | 41 | if (!modelFile) { 42 | throw new Error('未找到模型主配置文件,请确认模型文件是否完整。') 43 | } 44 | 45 | const modelPath = join(path, modelFile.name) 46 | 47 | const modelJSON = JSON.parse(await readTextFile(modelPath)) 48 | 49 | const modelSettings = new Cubism4ModelSettings({ 50 | ...modelJSON, 51 | url: convertFileSrc(modelPath), 52 | }) 53 | 54 | modelSettings.replaceFiles((file) => { 55 | return convertFileSrc(join(path, file)) 56 | }) 57 | 58 | this.model = await Live2DModel.from(modelSettings) 59 | 60 | this.app?.stage.addChild(this.model) 61 | 62 | const { motions, expressions } = modelSettings 63 | 64 | return { 65 | motions, 66 | expressions, 67 | } 68 | } 69 | 70 | public destroy() { 71 | this.model?.destroy() 72 | } 73 | 74 | public playMotion(group: string, index: number) { 75 | return this.model?.motion(group, index) 76 | } 77 | 78 | public playExpressions(index: number) { 79 | return this.model?.expression(index) 80 | } 81 | 82 | public getCoreModel() { 83 | const internalModel = this.model?.internalModel as Cubism4InternalModel 84 | 85 | return internalModel?.coreModel 86 | } 87 | 88 | public getParameterRange(id: string) { 89 | const coreModel = this.getCoreModel() 90 | 91 | const index = coreModel?.getParameterIndex(id) 92 | const min = coreModel?.getParameterMinimumValue(index) 93 | const max = coreModel?.getParameterMaximumValue(index) 94 | 95 | return { 96 | min, 97 | max, 98 | } 99 | } 100 | 101 | public setParameterValue(id: string, value: number) { 102 | const coreModel = this.getCoreModel() 103 | 104 | return coreModel?.setParameterValueById?.(id, Number(value)) 105 | } 106 | } 107 | 108 | const live2d = new Live2d() 109 | 110 | export default live2d 111 | -------------------------------------------------------------------------------- /src/utils/monitor.ts: -------------------------------------------------------------------------------- 1 | import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow' 2 | import { 3 | cursorPosition, 4 | monitorFromPoint, 5 | } from '@tauri-apps/api/window' 6 | 7 | export async function getCursorMonitor() { 8 | const appWindow = getCurrentWebviewWindow() 9 | 10 | const scaleFactor = await appWindow.scaleFactor() 11 | 12 | const point = await cursorPosition() 13 | 14 | const { x, y } = point.toLogical(scaleFactor) 15 | 16 | const monitor = await monitorFromPoint(x, y) 17 | 18 | if (!monitor) return 19 | 20 | return { 21 | ...monitor, 22 | cursorPosition: point, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/path.ts: -------------------------------------------------------------------------------- 1 | import type { LiteralUnion } from 'ant-design-vue/es/_util/type' 2 | 3 | import { sep } from '@tauri-apps/api/path' 4 | 5 | export function join(...paths: LiteralUnion<'resources' | 'left-keys' | 'right-keys' | 'background.png' | 'cover.png'>[]) { 6 | const joinPaths = paths.map((path) => { 7 | if (path.endsWith(sep())) { 8 | return path.slice(0, -1) 9 | } 10 | 11 | return path 12 | }) 13 | 14 | return joinPaths.join(sep()) 15 | } 16 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | 'success': 'var(--ant-green)', 44 | 'danger': 'var(--ant-red)', 45 | }, 46 | }, 47 | }) 48 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------