├── .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 | 
2 |
3 |
53 |
54 | | macOS | Window | Linux(x11) |
55 | | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
56 | |  |  |  |
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 |
92 |
93 |
94 |
95 | ## 贡献指南
96 |
97 | 感谢大家为 BongoCat 做出的宝贵贡献!如果你也希望为 BongoCat 做出贡献,请查阅[贡献指南](.github/CONTRIBUTING.md)。
98 |
99 |
100 |
101 |
102 |
103 | ## 历史星标
104 |
105 |
106 |
107 |
108 |
109 |
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 |
78 |
79 |
80 |
81 |
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 |
23 |
30 |
31 |
32 |
36 |
37 |
38 |
42 |
43 | {{ title }}
44 |
45 |
46 |
50 |
51 | {{ description }}
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/src/components/pro-list/index.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
15 |
19 | {{ title }}
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/components/update-app/index.vue:
--------------------------------------------------------------------------------
1 |
129 |
130 |
131 |
140 |
141 | {{ state.downloading ? downloadProgress : "立即更新" }}
142 |
143 |
144 |
149 |
150 | 更新版本:
151 |
152 | {{ state.update?.currentVersion }} 👉
153 |
156 | {{ state.update?.version }}
157 |
158 |
159 |
160 |
161 |
162 | 更新时间:
163 | {{ state.update?.date }}
164 |
165 |
166 |
167 | 更新日志:
168 |
169 |
173 |
174 |
175 |
176 |
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 |
77 |
84 |
![]()
85 |
86 |
87 |
88 |
![]()
93 |
94 |
![]()
99 |
100 |
104 |
105 | 重绘中...
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/src/pages/preference/components/about/index.vue:
--------------------------------------------------------------------------------
1 |
46 |
47 |
48 |
49 |
53 |
59 |
60 |
61 |
62 |

66 |
67 |
68 |
69 |
70 |
74 |
77 |
78 |
79 |
80 |
86 |
87 |
88 |
89 | {{ GITHUB_LINK }}
90 |
91 |
92 |
93 |
94 |
98 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/src/pages/preference/components/cat/index.vue:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
32 |
33 |
34 |
35 |
39 |
40 |
41 |
42 |
43 |
44 |
48 |
49 |
50 |
51 |
55 |
56 |
57 |
58 |
63 |
70 |
71 |
72 |
76 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/src/pages/preference/components/general/components/macos-permissions/index.vue:
--------------------------------------------------------------------------------
1 |
34 |
35 |
36 |
40 |
44 |
49 |
50 |
51 | 已授权
52 |
53 |
54 |
60 |
61 |
62 | 去授权
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/pages/preference/components/general/index.vue:
--------------------------------------------------------------------------------
1 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/pages/preference/components/model/components/float-menu/index.vue:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/pages/preference/components/model/components/upload/index.vue:
--------------------------------------------------------------------------------
1 |
85 |
86 |
87 |
93 |
94 |
95 |
点击或拖动至此区域导入
96 |
97 |
98 |
--------------------------------------------------------------------------------
/src/pages/preference/components/model/index.vue:
--------------------------------------------------------------------------------
1 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
55 |
59 |
60 |
64 |
65 |
66 |
67 |
72 |
73 |
77 |
78 |
79 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/src/pages/preference/index.vue:
--------------------------------------------------------------------------------
1 |
46 |
47 |
48 |
49 |
54 |
55 |
56 |

60 |
61 |
62 |
{{ appStore.name }}
63 |
64 |
65 |
66 |
73 |
77 |
78 |
{{ item.label }}
79 |
80 |
81 |
82 |
83 |
90 |
91 |
92 |
93 |
94 |
95 |
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