14 Commits

29 changed files with 1897 additions and 1888 deletions

View File

@@ -1,7 +1,7 @@
name: 🐞 Bug 报告
title: '[bug] '
description: 报告一个 Bug
labels: 🐞 bug
labels: bug
body:
- type: markdown
attributes:

View File

@@ -1,7 +1,7 @@
name: 💡 功能请求
title: '[feat] '
description: 提出一个想法
labels: 💡 feature request
labels: feature request
body:
- type: textarea
id: problem

View File

@@ -2,6 +2,8 @@
// Disable the default formatter, use eslint instead
"prettier.enable": false,
"eslint.format.enable": true,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",

312
Cargo.lock generated
View File

@@ -184,9 +184,9 @@ dependencies = [
[[package]]
name = "async-io"
version = "2.4.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059"
checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3"
dependencies = [
"async-lock",
"cfg-if",
@@ -195,7 +195,7 @@ dependencies = [
"futures-lite",
"parking",
"polling",
"rustix 0.38.44",
"rustix 1.0.7",
"slab",
"tracing",
"windows-sys 0.59.0",
@@ -214,9 +214,9 @@ dependencies = [
[[package]]
name = "async-process"
version = "2.3.0"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb"
checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc"
dependencies = [
"async-channel",
"async-io",
@@ -227,7 +227,7 @@ dependencies = [
"cfg-if",
"event-listener",
"futures-lite",
"rustix 0.38.44",
"rustix 1.0.7",
"tracing",
]
@@ -244,9 +244,9 @@ dependencies = [
[[package]]
name = "async-signal"
version = "0.2.10"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3"
checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d"
dependencies = [
"async-io",
"async-lock",
@@ -254,7 +254,7 @@ dependencies = [
"cfg-if",
"futures-core",
"futures-io",
"rustix 0.38.44",
"rustix 1.0.7",
"signal-hook-registry",
"slab",
"windows-sys 0.59.0",
@@ -464,6 +464,7 @@ dependencies = [
"tauri-plugin-custom-window",
"tauri-plugin-dialog",
"tauri-plugin-fs",
"tauri-plugin-global-shortcut",
"tauri-plugin-log",
"tauri-plugin-macos-permissions",
"tauri-plugin-opener",
@@ -612,9 +613,9 @@ dependencies = [
[[package]]
name = "camino"
version = "1.1.9"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab"
dependencies = [
"serde",
]
@@ -654,9 +655,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.24"
version = "1.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7"
checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951"
dependencies = [
"shlex",
]
@@ -747,7 +748,7 @@ dependencies = [
"bitflags 2.9.1",
"block",
"cocoa-foundation 0.2.1",
"core-foundation 0.10.0",
"core-foundation 0.10.1",
"core-graphics 0.24.0",
"foreign-types 0.5.0",
"libc",
@@ -776,7 +777,7 @@ checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d"
dependencies = [
"bitflags 2.9.1",
"block",
"core-foundation 0.10.0",
"core-foundation 0.10.1",
"core-graphics-types 0.2.0",
"objc",
]
@@ -828,9 +829,9 @@ dependencies = [
[[package]]
name = "core-foundation"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
dependencies = [
"core-foundation-sys",
"libc",
@@ -862,7 +863,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
dependencies = [
"bitflags 2.9.1",
"core-foundation 0.10.0",
"core-foundation 0.10.1",
"core-graphics-types 0.2.0",
"foreign-types 0.5.0",
"libc",
@@ -886,7 +887,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [
"bitflags 2.9.1",
"core-foundation 0.10.0",
"core-foundation 0.10.1",
"libc",
]
@@ -1157,9 +1158,9 @@ dependencies = [
[[package]]
name = "dlopen2_derive"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54"
checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4"
dependencies = [
"proc-macro2",
"quote",
@@ -1216,16 +1217,16 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "embed-resource"
version = "3.0.2"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbc6e0d8e0c03a655b53ca813f0463d2c956bc4db8138dbc89f120b066551e3"
checksum = "e8fe7d068ca6b3a5782ca5ec9afc244acd99dd441e4686a83b1c3973aba1d489"
dependencies = [
"cc",
"memchr",
"rustc_version",
"toml",
"vswhom",
"winreg 0.52.0",
"winreg 0.55.0",
]
[[package]]
@@ -1864,6 +1865,24 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "global-hotkey"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7"
dependencies = [
"crossbeam-channel",
"keyboard-types",
"objc2 0.6.1",
"objc2-app-kit",
"once_cell",
"serde",
"thiserror 2.0.12",
"windows-sys 0.59.0",
"x11rb",
"xkeysym",
]
[[package]]
name = "gobject-sys"
version = "0.18.0"
@@ -1962,9 +1981,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.4.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
[[package]]
name = "hex"
@@ -2065,22 +2084,26 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
"webpki-roots 1.0.0",
"webpki-roots",
]
[[package]]
name = "hyper-util"
version = "0.1.12"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710"
checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8"
dependencies = [
"base64 0.22.1",
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"http",
"http-body",
"hyper",
"ipnet",
"libc",
"percent-encoding",
"pin-project-lite",
"socket2",
"tokio",
@@ -2305,6 +2328,16 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "iri-string"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "is-docker"
version = "0.2.0"
@@ -2529,9 +2562,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
[[package]]
name = "lock_api"
version = "0.4.12"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
@@ -2654,13 +2687,13 @@ dependencies = [
[[package]]
name = "mio"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -3160,9 +3193,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
version = "0.12.3"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
dependencies = [
"lock_api",
"parking_lot_core",
@@ -3170,9 +3203,9 @@ dependencies = [
[[package]]
name = "parking_lot_core"
version = "0.9.10"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
@@ -3394,15 +3427,15 @@ dependencies = [
[[package]]
name = "polling"
version = "3.7.4"
version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50"
dependencies = [
"cfg-if",
"concurrent-queue",
"hermit-abi",
"pin-project-lite",
"rustix 0.38.44",
"rustix 1.0.7",
"tracing",
"windows-sys 0.59.0",
]
@@ -3439,9 +3472,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "prettyplease"
version = "0.2.32"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6"
checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d"
dependencies = [
"proc-macro2",
"syn 2.0.101",
@@ -3839,9 +3872,9 @@ dependencies = [
[[package]]
name = "reqwest"
version = "0.12.15"
version = "0.12.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -3862,7 +3895,6 @@ dependencies = [
"pin-project-lite",
"quinn",
"rustls",
"rustls-pemfile",
"rustls-pki-types",
"serde",
"serde_json",
@@ -3872,14 +3904,14 @@ dependencies = [
"tokio-rustls",
"tokio-util",
"tower",
"tower-http",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"webpki-roots 0.26.11",
"windows-registry",
"webpki-roots",
]
[[package]]
@@ -4027,15 +4059,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.12.0"
@@ -4361,9 +4384,9 @@ checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "socket2"
version = "0.5.9"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
"libc",
"windows-sys 0.52.0",
@@ -4589,7 +4612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82"
dependencies = [
"bitflags 2.9.1",
"core-foundation 0.10.0",
"core-foundation 0.10.1",
"core-graphics 0.24.0",
"crossbeam-channel",
"dispatch",
@@ -4774,12 +4797,12 @@ dependencies = [
[[package]]
name = "tauri-nspanel"
version = "2.0.1"
source = "git+https://github.com/ahkohd/tauri-nspanel?branch=v2#d4b9df797959f8fa4701e8a20ff69d9605bb66e9"
source = "git+https://github.com/ahkohd/tauri-nspanel?branch=v2#dc1563b2c71d3699dc67028080e43aade0575bcf"
dependencies = [
"bitflags 2.9.1",
"block",
"cocoa 0.26.1",
"core-foundation 0.10.0",
"core-foundation 0.10.1",
"core-graphics 0.24.0",
"objc",
"objc-foundation",
@@ -4883,6 +4906,21 @@ dependencies = [
"url",
]
[[package]]
name = "tauri-plugin-global-shortcut"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31919f3c07bcb585afef217c0c33cde80da9ebccf5b8e2c90e0e0a535b14ab47"
dependencies = [
"global-hotkey",
"log",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"thiserror 2.0.12",
]
[[package]]
name = "tauri-plugin-log"
version = "2.4.0"
@@ -4962,9 +5000,9 @@ dependencies = [
[[package]]
name = "tauri-plugin-pinia"
version = "3.4.0"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e87882a5a390b91bd7f785c382cd374b4a1b32ce3429cec871b16ee1ff1f8cb8"
checksum = "bec0cb197a26a9b468679887d85b38ad1860cf032d5bc2fd755d77f7e704b838"
dependencies = [
"serde",
"tauri",
@@ -5095,9 +5133,9 @@ dependencies = [
[[package]]
name = "tauri-store"
version = "0.10.5"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d35f80125c58fcbc5d408d288f7bc7d89b585c4ceb474217c553d9c0afc3963"
checksum = "f7981255eefa2868d49af173f27f0171e6e4a3c580530334f31a7be8b25446c8"
dependencies = [
"dashmap",
"futures",
@@ -5113,9 +5151,9 @@ dependencies = [
[[package]]
name = "tauri-store-macros"
version = "0.10.5"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a1c7a0b908a8d874ac2c055d757273c102d386d06690f56fe6d7a93bbc4ea60"
checksum = "fef3bf6f34307c6185436ea109c6fd6c01bfb9636c16902cd7e8abcc243fbecc"
dependencies = [
"proc-macro2",
"quote",
@@ -5124,12 +5162,11 @@ dependencies = [
[[package]]
name = "tauri-store-utils"
version = "0.4.2"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bd3d928e03a75d07774ae2e4b754eb32cbf87e6154b3c7b7f1facd40a6e9497"
checksum = "baed2bbdce143a0b6e8105a0c9c7d9bfaa9c5689c3632bff12085c7209365d3f"
dependencies = [
"bon",
"semver",
"serde",
"serde_json",
"tauri",
@@ -5327,14 +5364,14 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.45.0"
version = "1.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio 1.0.3",
"mio 1.0.4",
"pin-project-lite",
"signal-hook-registry",
"socket2",
@@ -5455,6 +5492,24 @@ dependencies = [
"tower-service",
]
[[package]]
name = "tower-http"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
dependencies = [
"bitflags 2.9.1",
"bytes",
"futures-util",
"http",
"http-body",
"iri-string",
"pin-project-lite",
"tower",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-layer"
version = "0.3.3"
@@ -5971,15 +6026,6 @@ dependencies = [
"system-deps",
]
[[package]]
name = "webpki-roots"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.0",
]
[[package]]
name = "webpki-roots"
version = "1.0.0"
@@ -6115,7 +6161,7 @@ dependencies = [
"windows-interface",
"windows-link",
"windows-result",
"windows-strings 0.4.2",
"windows-strings",
]
[[package]]
@@ -6167,17 +6213,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-registry"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
dependencies = [
"windows-result",
"windows-strings 0.3.1",
"windows-targets 0.53.0",
]
[[package]]
name = "windows-result"
version = "0.3.4"
@@ -6187,15 +6222,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
@@ -6280,29 +6306,13 @@ dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
dependencies = [
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
"windows_i686_gnullvm 0.53.0",
"windows_i686_msvc 0.53.0",
"windows_x86_64_gnu 0.53.0",
"windows_x86_64_gnullvm 0.53.0",
"windows_x86_64_msvc 0.53.0",
]
[[package]]
name = "windows-threading"
version = "0.1.0"
@@ -6339,12 +6349,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
@@ -6363,12 +6367,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
@@ -6387,24 +6385,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
@@ -6423,12 +6409,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
@@ -6447,12 +6427,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
@@ -6471,12 +6445,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
@@ -6495,12 +6463,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "winnow"
version = "0.5.40"
@@ -6530,12 +6492,12 @@ dependencies = [
[[package]]
name = "winreg"
version = "0.52.0"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -6673,6 +6635,12 @@ dependencies = [
"rustix 1.0.7",
]
[[package]]
name = "xkeysym"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
[[package]]
name = "yoke"
version = "0.8.0"

View File

@@ -72,9 +72,26 @@
- 适配 macOS、Windows 和 Linux(x11)。
- 根据据键盘或鼠标操作,同步移动鼠标或敲击键盘。
- 支持导入自定义模型,自由打造专属猫咪形象。
- 完全开源,代码公开透明,绝不收集任何用户数据。
- 支持离线运行,无需联网,保护用户隐私。
## 更多模型
你可以在这个仓库中探索、下载更多猫咪模型,或提交你的创作,与大家一起分享:
📦 [Awesome-BongoCat](https://github.com/ayangweb/Awesome-BongoCat)
## 社区交流
<a href="https://qm.qq.com/q/AS3gNv2Vzy">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/6428b6ff-0b39-4e16-a750-bad6f6b376e9" />
<source media="(prefers-color-scheme: light)" srcset="https://github.com/user-attachments/assets/e50883e2-0be7-4eea-9879-e725fa1dffd2" />
<img alt="QQ Group" src="https://github.com/user-attachments/assets/e50883e2-0be7-4eea-9879-e725fa1dffd2" height="250" />
</picture>
</a>
## 贡献指南
感谢大家为 BongoCat 做出的宝贵贡献!如果你也希望为 BongoCat 做出贡献,请查阅[贡献指南](.github/CONTRIBUTING.md)。

View File

@@ -27,43 +27,44 @@
"@tauri-apps/plugin-clipboard-manager": "~2.2.2",
"@tauri-apps/plugin-dialog": "~2.2.2",
"@tauri-apps/plugin-fs": "~2.3.0",
"@tauri-apps/plugin-global-shortcut": "~2.2.1",
"@tauri-apps/plugin-log": "~2.3.1",
"@tauri-apps/plugin-opener": "~2.2.7",
"@tauri-apps/plugin-os": "^2.2.1",
"@tauri-apps/plugin-process": "^2.2.1",
"@tauri-apps/plugin-updater": "~2.7.1",
"@tauri-store/pinia": "^3.4.0",
"@vueuse/core": "^13.2.0",
"@tauri-store/pinia": "^3.7.0",
"@vueuse/core": "^13.3.0",
"ant-design-vue": "^4.2.6",
"dayjs": "^1.11.13",
"easy-live2d": "^0.3.2",
"es-toolkit": "^1.38.0",
"is-url": "^1.2.4",
"nanoid": "^5.1.5",
"pinia": "^3.0.2",
"pixi-live2d-display": "^0.4.0",
"pixi.js": "^6.5.10",
"pinia": "^3.0.3",
"pixi.js": "^8.10.1",
"tauri-plugin-macos-permissions-api": "^2.3.0",
"vue": "^3.5.14",
"vue": "^3.5.16",
"vue-markdown-render": "^2.2.1",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@antfu/eslint-config": "^4.13.2",
"@antfu/eslint-config": "^4.13.3",
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@iconify-json/iconamoon": "^1.2.2",
"@iconify-json/solar": "^1.2.2",
"@tauri-apps/cli": "^2.5.0",
"@types/is-url": "^1.2.32",
"@types/node": "^22.15.21",
"@unocss/eslint-plugin": "^66.1.2",
"@types/node": "^22.15.29",
"@unocss/eslint-plugin": "^66.1.3",
"@vitejs/plugin-vue": "^5.2.4",
"eslint": "^9.27.0",
"eslint": "^9.28.0",
"eslint-plugin-format": "^1.0.1",
"lint-staged": "^15.5.2",
"npm-run-all": "^4.1.5",
"release-it": "^18.1.2",
"sass": "^1.89.0",
"sass": "^1.89.1",
"simple-git-hooks": "^2.13.0",
"tsx": "^4.19.4",
"typescript": "~5.6.3",

2630
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -37,6 +37,7 @@ tauri-plugin-dialog = "2"
tauri-plugin-fs = "2"
fs_extra = "1"
tauri-plugin-clipboard-manager = "2"
tauri-plugin-global-shortcut = "2"
[target."cfg(target_os = \"macos\")".dependencies]
tauri-nspanel.workspace = true

View File

@@ -42,6 +42,9 @@
"**/*"
]
},
"clipboard-manager:allow-write-text"
"clipboard-manager:allow-write-text",
"global-shortcut:allow-is-registered",
"global-shortcut:allow-register",
"global-shortcut:allow-unregister"
]
}

View File

@@ -1,9 +1,5 @@
use tauri::{AppHandle, Emitter, EventTarget, WebviewWindow};
use tauri_nspanel::{
WebviewWindowExt,
cocoa::appkit::{NSMainMenuWindowLevel, NSWindowCollectionBehavior},
panel_delegate,
};
use tauri_nspanel::{WebviewWindowExt, cocoa::appkit::NSWindowCollectionBehavior, panel_delegate};
use tauri_plugin_custom_window::MAIN_WINDOW_LABEL;
#[allow(non_upper_case_globals)]
@@ -26,8 +22,6 @@ pub fn platform(
let panel = main_window.to_panel().unwrap();
panel.set_level(NSMainMenuWindowLevel);
panel.set_style_mask(NSWindowStyleMaskNonActivatingPanel | NSResizableWindowMask);
panel.set_collection_behaviour(

View File

@@ -23,7 +23,7 @@ pub fn default(
main_window: WebviewWindow,
preference_window: WebviewWindow,
) {
#[cfg(any(dev, debug_assertions))]
#[cfg(debug_assertions)]
main_window.open_devtools();
platform(app_handle, main_window.clone(), preference_window.clone());

View File

@@ -47,6 +47,7 @@ pub fn run() {
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
.on_window_event(|window, event| match event {
WindowEvent::CloseRequested { api, .. } => {
let _ = window.hide();

View File

@@ -29,7 +29,9 @@
"url": "index.html/#/preference",
"visible": false,
"titleBarStyle": "Overlay",
"hiddenTitle": true
"hiddenTitle": true,
"minWidth": 800,
"minHeight": 600
}
],
"security": {

View File

@@ -19,12 +19,14 @@ import { useAppStore } from './stores/app'
import { useCatStore } from './stores/cat'
import { useGeneralStore } from './stores/general'
import { useModelStore } from './stores/model'
import { useShortcutStore } from './stores/shortcut.ts'
const { generateColorVars } = useThemeVars()
const appStore = useAppStore()
const modelStore = useModelStore()
const catStore = useCatStore()
const generalStore = useGeneralStore()
const shortcutStore = useShortcutStore()
const appWindow = getCurrentWebviewWindow()
const { isRestored, restoreState } = useWindowState()
@@ -35,6 +37,8 @@ onMounted(async () => {
await modelStore.$tauri.start()
await catStore.$tauri.start()
await generalStore.$tauri.start()
await shortcutStore.$tauri.start()
catStore.visible = true
restoreState()
})

View File

@@ -2,18 +2,14 @@
import { Flex } from 'ant-design-vue'
import { computed, useSlots } from 'vue'
const { title, icon, description, vertical } = defineProps<{
const { title, description, vertical } = defineProps<{
title: string
icon?: string
description?: string
vertical?: boolean
}>()
const slots = useSlots()
const hasIcon = computed(() => {
return icon || slots.icon
})
const hasDescription = computed(() => {
return description || slots.description
})
@@ -28,23 +24,13 @@ const hasDescription = computed(() => {
:vertical="vertical"
>
<Flex align="center">
<slot name="icon">
<div
class="text-4"
:class="icon"
/>
</slot>
<Flex
:class="{ 'ml-4': hasIcon }"
vertical
>
<Flex vertical>
<div class="text-sm font-medium">
{{ title }}
</div>
<div
class="text-xs [&_a]:(active:text-color-primary-7 hover:text-color-primary-5 text-color-3) text-color-3"
class="break-all text-xs [&_a]:(active:text-color-primary-7 hover:text-color-primary-5 text-color-3) text-color-3"
:class="{ 'mt-2': hasDescription }"
>
<slot name="description">

View File

@@ -0,0 +1,127 @@
<script setup lang="ts">
import type { Key } from '@/utils/keyboard'
import { find, map, remove, some, split } from 'es-toolkit/compat'
import { ref, useTemplateRef, watch } from 'vue'
import ProListItem from '@/components/pro-list-item/index.vue'
import { keys, modifierKeys, standardKeys } from '@/utils/keyboard'
const props = defineProps<{
title: string
description?: string
}>()
const modelValue = defineModel<string>()
const shortcutInputRef = useTemplateRef('shortcutInput')
const isFocusing = ref(false)
const isHovering = ref(false)
const pressedKeys = ref<Key[]>([])
watch(modelValue, () => {
parseModelValue()
}, { immediate: true })
function parseModelValue() {
if (!modelValue.value) {
return pressedKeys.value = []
}
pressedKeys.value = split(modelValue.value, '+').map((tauriKey) => {
return find(keys, { tauriKey })!
})
}
function getEventKey(event: KeyboardEvent) {
const { key, code } = event
const eventKey = key.replace('Meta', 'Command')
const isModifierKey = some(modifierKeys, { eventKey })
return isModifierKey ? eventKey : code
}
function isValidShortcut() {
if (pressedKeys.value?.[0]?.eventKey?.startsWith('F')) {
return true
}
const hasModifierKey = some(pressedKeys.value, ({ eventKey }) => {
return some(modifierKeys, { eventKey })
})
const hasStandardKey = some(pressedKeys.value, ({ eventKey }) => {
return some(standardKeys, { eventKey })
})
return hasModifierKey && hasStandardKey
}
function handleFocus() {
isFocusing.value = true
pressedKeys.value = []
}
function handleBlur() {
isFocusing.value = false
if (!isValidShortcut()) {
return parseModelValue()
}
modelValue.value = map(pressedKeys.value, 'tauriKey').join('+')
}
function handleKeyDown(event: KeyboardEvent) {
const eventKey = getEventKey(event)
const matched = find(keys, { eventKey })
const isInvalid = !matched
const isDuplicate = some(pressedKeys.value, { eventKey })
if (isInvalid || isDuplicate) return
pressedKeys.value.push(matched)
if (isValidShortcut()) {
shortcutInputRef.value?.blur()
}
}
function handleKeyUp(event: KeyboardEvent) {
remove(pressedKeys.value, { eventKey: getEventKey(event) })
}
</script>
<template>
<ProListItem v-bind="props">
<div
ref="shortcutInput"
align="center"
class="relative h-8 min-w-32 flex cursor-text items-center justify-center b b-color-1 hover:b-primary-5 rounded-md b-solid px-2.5 text-color-3 outline-none transition focus:(b-primary shadow-[0_0_0_2px_rgba(5,145,255,0.1)])"
justify="center"
:tabindex="0"
@blur="handleBlur"
@focus="handleFocus"
@keydown="handleKeyDown"
@keyup="handleKeyUp"
@mouseout="isHovering = false"
@mouseover="isHovering = true"
>
<span v-if="pressedKeys.length === 0">
{{ isFocusing ? '按下录制快捷键' : '点击录制快捷键' }}
</span>
<span class="text-primary font-bold">
{{ map(pressedKeys, 'symbol').join(' ') }}
</span>
<div
class="i-iconamoon:close-circle-1 absolute right-2 cursor-pointer text-4 transition hover:text-primary"
:hidden="isFocusing || !isHovering || pressedKeys.length === 0"
@mousedown.prevent="modelValue = ''"
/>
</div>
</ProListItem>
</template>

View File

@@ -43,11 +43,11 @@ export function useModel() {
await resolveResource(path)
const data = await live2d.load(path)
await live2d.load(path)
handleResize()
// handleResize()
Object.assign(modelStore, data)
// Object.assign(modelStore, data)
} catch (error) {
message.error(String(error))
}

View File

@@ -1,7 +1,7 @@
import { CheckMenuItem, MenuItem, PredefinedMenuItem, Submenu } from '@tauri-apps/api/menu'
import { range } from 'es-toolkit'
import { hideWindow, showWindow } from '@/plugins/window'
import { showWindow } from '@/plugins/window'
import { useCatStore } from '@/stores/cat'
import { isMac } from '@/utils/platform'
@@ -66,12 +66,6 @@ export function useSharedMenu() {
MenuItem.new({
text: catStore.visible ? '隐藏猫咪' : '显示猫咪',
action: () => {
if (catStore.visible) {
hideWindow('main')
} else {
showWindow('main')
}
catStore.visible = !catStore.visible
},
}),

View File

@@ -0,0 +1,33 @@
import type { ShortcutHandler } from '@tauri-apps/plugin-global-shortcut'
import type { Ref } from 'vue'
import {
isRegistered,
register,
unregister,
} from '@tauri-apps/plugin-global-shortcut'
import { ref, watch } from 'vue'
export function useTauriKeyPress(shortcut: Ref<string, string>, callback: ShortcutHandler) {
const oldShortcut = ref(shortcut.value)
watch(shortcut, async (value) => {
if (oldShortcut.value) {
const registered = await isRegistered(oldShortcut.value)
if (registered) {
await unregister(oldShortcut.value)
}
}
if (!value) return
await register(value, (event) => {
if (event.state === 'Released') return
callback(event)
})
oldShortcut.value = value
}, { immediate: true })
}

View File

@@ -8,7 +8,7 @@ import { onUnmounted, ref, watch } from 'vue'
import { useDevice } from '@/composables/useDevice'
import { useModel } from '@/composables/useModel'
import { useSharedMenu } from '@/composables/useSharedMenu'
import { setAlwaysOnTop } from '@/plugins/window'
import { hideWindow, setAlwaysOnTop, showWindow } from '@/plugins/window'
import { useCatStore } from '@/stores/cat'
import { useModelStore } from '@/stores/model'
import { join } from '@/utils/path'
@@ -19,7 +19,6 @@ const { backgroundImage, handleDestroy, handleResize, handleMouseDown, handleMou
const catStore = useCatStore()
const { getSharedMenu } = useSharedMenu()
const modelStore = useModelStore()
const resizing = ref(false)
onUnmounted(handleDestroy)
@@ -48,6 +47,10 @@ watch(pressedRightKeys, (keys) => {
handleKeyDown('right', keys.length > 0)
})
watch(() => catStore.visible, async (value) => {
value ? showWindow() : hideWindow()
})
watch(() => catStore.penetrable, (value) => {
appWindow.setIgnoreCursorEvents(value)
}, { immediate: true })

View File

@@ -40,7 +40,7 @@ async function copyInfo() {
}
function feedbackIssue() {
openUrl(`${GITHUB_LINK}/issues/new`)
openUrl(`${GITHUB_LINK}/issues/new/choose`)
}
</script>

View File

@@ -0,0 +1,68 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import ProList from '@/components/pro-list/index.vue'
import ProShortcut from '@/components/pro-shortcut/index.vue'
import { useTauriKeyPress } from '@/composables/useTauriKeyPress'
import { toggleWindowVisible } from '@/plugins/window'
import { useCatStore } from '@/stores/cat'
import { useShortcutStore } from '@/stores/shortcut.ts'
const shortcutStore = useShortcutStore()
const { visibleCat, visiblePreference, mirrorMode, penetrable, alwaysOnTop } = storeToRefs(shortcutStore)
const catStore = useCatStore()
useTauriKeyPress(visibleCat, () => {
catStore.visible = !catStore.visible
})
useTauriKeyPress(visiblePreference, () => {
toggleWindowVisible('preference')
})
useTauriKeyPress(mirrorMode, () => {
catStore.mirrorMode = !catStore.mirrorMode
})
useTauriKeyPress(penetrable, () => {
catStore.penetrable = !catStore.penetrable
})
useTauriKeyPress(alwaysOnTop, () => {
catStore.alwaysOnTop = !catStore.alwaysOnTop
})
</script>
<template>
<ProList title="快捷键">
<ProShortcut
v-model="shortcutStore.visibleCat"
description="切换猫咪窗口的显示与隐藏"
title="打开猫咪"
/>
<ProShortcut
v-model="shortcutStore.visiblePreference"
description="切换偏好设置窗口的显示与隐藏"
title="打开偏好设置"
/>
<ProShortcut
v-model="shortcutStore.mirrorMode"
description="切换猫咪的镜像模式"
title="镜像模式"
/>
<ProShortcut
v-model="shortcutStore.penetrable"
description="切换猫咪窗口是否可穿透"
title="窗口穿透"
/>
<ProShortcut
v-model="shortcutStore.alwaysOnTop"
description="切换猫咪窗口是否置顶"
title="窗口置顶"
/>
</ProList>
</template>

View File

@@ -6,6 +6,7 @@ import About from './components/about/index.vue'
import Cat from './components/cat/index.vue'
import General from './components/general/index.vue'
import Model from './components/model/index.vue'
import Shortcut from './components/shortcut/index.vue'
import UpdateApp from '@/components/update-app/index.vue'
import { useTray } from '@/composables/useTray'
@@ -36,6 +37,11 @@ const menus = [
icon: 'i-solar:magic-stick-3-bold',
component: Model,
},
{
label: '快捷键',
icon: 'i-solar:keyboard-bold',
component: Shortcut,
},
{
label: '关于',
icon: 'i-solar:info-circle-bold',
@@ -47,7 +53,7 @@ const menus = [
<template>
<Flex class="h-screen">
<div
class="h-full w-30 flex flex-col items-center gap-4 bg-gradient-from-primary-1 bg-gradient-to-black/1 bg-gradient-linear"
class="h-full w-30 flex flex-col items-center gap-4 overflow-auto bg-gradient-from-primary-1 bg-gradient-to-black/1 bg-gradient-linear"
:class="[isMac ? 'pt-8' : 'pt-4']"
data-tauri-drag-region
>
@@ -68,7 +74,7 @@ const menus = [
:key="item.label"
class="size-20 flex flex-col cursor-pointer items-center justify-center gap-2 rounded-lg hover:bg-color-7 text-color-3 transition"
:class="{ 'bg-white! text-primary-5 font-bold': current === index }"
@mousedown="current = index"
@click="current = index"
>
<div
class="size-8"

View File

@@ -1,5 +1,6 @@
import { invoke } from '@tauri-apps/api/core'
import { emit } from '@tauri-apps/api/event'
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
import { LISTEN_KEY } from '../constants'
@@ -30,3 +31,17 @@ export function hideWindow(label?: WindowLabel) {
export function setAlwaysOnTop(alwaysOnTop: boolean) {
invoke(COMMAND.SET_ALWAYS_ON_TOP, { alwaysOnTop })
}
export async function toggleWindowVisible(label?: WindowLabel) {
const appWindow = getCurrentWebviewWindow()
if (appWindow.label !== label) return
const visible = await appWindow.isVisible()
if (visible) {
return hideWindow(label)
}
return showWindow(label)
}

View File

@@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useCatStore = defineStore('cat', () => {
const visible = ref(true)
const visible = ref(false)
const mirrorMode = ref(false)
const singleMode = ref(false)
const mouseMirror = ref(false)

20
src/stores/shortcut.ts Normal file
View File

@@ -0,0 +1,20 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export type HotKey = 'visibleCat' | 'mirrorMode' | 'penetrable' | 'alwaysOnTop'
export const useShortcutStore = defineStore('shortcut', () => {
const visibleCat = ref('')
const visiblePreference = ref('')
const mirrorMode = ref('')
const penetrable = ref('')
const alwaysOnTop = ref('')
return {
visibleCat,
visiblePreference,
mirrorMode,
penetrable,
alwaysOnTop,
}
})

276
src/utils/keyboard.ts Normal file
View File

@@ -0,0 +1,276 @@
import { isMac } from './platform'
export interface Key {
eventKey: string
tauriKey?: string
symbol?: string
}
export const modifierKeys: Key[] = [
{
eventKey: 'Shift',
symbol: isMac ? '⇧' : 'Shift',
},
{
eventKey: 'Control',
symbol: isMac ? '⌃' : 'Ctrl',
},
{
eventKey: 'Alt',
symbol: isMac ? '⌥' : 'Alt',
},
{
eventKey: 'Command',
symbol: isMac ? '⌘' : 'Super',
},
].map((item) => {
return { ...item, tauriKey: item.eventKey }
})
export const standardKeys: Key[] = [
// 第一排
{
eventKey: 'Escape',
symbol: isMac ? '⎋' : 'Esc',
},
{
eventKey: 'F1',
},
{
eventKey: 'F2',
},
{
eventKey: 'F3',
},
{
eventKey: 'F4',
},
{
eventKey: 'F5',
},
{
eventKey: 'F6',
},
{
eventKey: 'F7',
},
{
eventKey: 'F8',
},
{
eventKey: 'F9',
},
{
eventKey: 'F10',
},
{
eventKey: 'F11',
},
{
eventKey: 'F12',
}, // 第二排
{
eventKey: 'Backquote',
symbol: '`',
},
{
eventKey: 'Digit1',
},
{
eventKey: 'Digit2',
},
{
eventKey: 'Digit3',
},
{
eventKey: 'Digit4',
},
{
eventKey: 'Digit5',
},
{
eventKey: 'Digit6',
},
{
eventKey: 'Digit7',
},
{
eventKey: 'Digit8',
},
{
eventKey: 'Digit9',
},
{
eventKey: 'Digit0',
},
{
eventKey: 'Minus',
tauriKey: '-',
symbol: '-',
},
{
eventKey: 'Equal',
tauriKey: '=',
symbol: '=',
},
{
eventKey: 'Backspace',
symbol: isMac ? '⌫' : void 0,
},
// 第三排
{
eventKey: 'Tab',
symbol: isMac ? '⇥' : void 0,
},
{
eventKey: 'KeyQ',
},
{
eventKey: 'KeyW',
},
{
eventKey: 'KeyE',
},
{
eventKey: 'KeyR',
},
{
eventKey: 'KeyT',
},
{
eventKey: 'KeyY',
},
{
eventKey: 'KeyU',
},
{
eventKey: 'KeyI',
},
{
eventKey: 'KeyO',
},
{
eventKey: 'KeyP',
},
{
eventKey: 'BracketLeft',
symbol: '[',
},
{
eventKey: 'BracketRight',
symbol: ']',
},
{
eventKey: 'Backslash',
symbol: '\\',
},
// 第四排
{
eventKey: 'KeyA',
},
{
eventKey: 'KeyS',
},
{
eventKey: 'KeyD',
},
{
eventKey: 'KeyF',
},
{
eventKey: 'KeyG',
},
{
eventKey: 'KeyH',
},
{
eventKey: 'KeyJ',
},
{
eventKey: 'KeyK',
},
{
eventKey: 'KeyL',
},
{
eventKey: 'Semicolon',
symbol: ';',
},
{
eventKey: 'Quote',
symbol: '\'',
},
{
eventKey: 'Enter',
symbol: isMac ? '↩︎' : void 0,
},
// 第五排
{
eventKey: 'KeyZ',
},
{
eventKey: 'KeyX',
},
{
eventKey: 'KeyC',
},
{
eventKey: 'KeyV',
},
{
eventKey: 'KeyB',
},
{
eventKey: 'KeyN',
},
{
eventKey: 'KeyM',
},
{
eventKey: 'Comma',
symbol: ',',
},
{
eventKey: 'Period',
symbol: '.',
},
{
eventKey: 'Slash',
symbol: '/',
},
// 第六排
{
eventKey: 'Space',
symbol: isMac ? '␣' : void 0,
},
// 方向键
{
eventKey: 'ArrowUp',
symbol: '↑',
},
{
eventKey: 'ArrowDown',
symbol: '↓',
},
{
eventKey: 'ArrowLeft',
symbol: '←',
},
{
eventKey: 'ArrowRight',
symbol: '→',
},
].map((item) => {
const { eventKey } = item
item.symbol ??= eventKey
item.tauriKey ??= eventKey
if (eventKey.startsWith('Digit') || eventKey.startsWith('Key')) {
item.tauriKey = item.symbol = eventKey.slice(-1)
}
return item
})
export const keys = modifierKeys.concat(standardKeys)

110
src/utils/live2d copy.ts Normal file
View File

@@ -0,0 +1,110 @@
import type { Cubism4InternalModel } from 'pixi-live2d-display'
import { convertFileSrc } from '@tauri-apps/api/core'
import { readDir, readTextFile } from '@tauri-apps/plugin-fs'
import { Cubism4ModelSettings, Live2DModel } from 'pixi-live2d-display'
import { Application, Ticker } from 'pixi.js'
import { join } from './path'
Live2DModel.registerTicker(Ticker)
class Live2d {
private app: Application | null = null
public model: Live2DModel | null = null
constructor() { }
private mount() {
const view = document.getElementById('live2dCanvas') as HTMLCanvasElement
this.app = new Application({
view,
resizeTo: window,
backgroundAlpha: 0,
autoDensity: true,
resolution: devicePixelRatio,
})
}
public async load(path: string) {
if (!this.app) {
this.mount()
}
this.destroy()
const files = await readDir(path)
const modelFile = files.find(file => file.name.endsWith('.model3.json'))
if (!modelFile) {
throw new Error('未找到模型主配置文件,请确认模型文件是否完整。')
}
const modelPath = join(path, modelFile.name)
const modelJSON = JSON.parse(await readTextFile(modelPath))
const modelSettings = new Cubism4ModelSettings({
...modelJSON,
url: convertFileSrc(modelPath),
})
modelSettings.replaceFiles((file) => {
return convertFileSrc(join(path, file))
})
this.model = await Live2DModel.from(modelSettings)
this.app?.stage.addChild(this.model)
const { motions, expressions } = modelSettings
return {
motions,
expressions,
}
}
public destroy() {
this.model?.destroy()
}
public playMotion(group: string, index: number) {
return this.model?.motion(group, index)
}
public playExpressions(index: number) {
return this.model?.expression(index)
}
public getCoreModel() {
const internalModel = this.model?.internalModel as Cubism4InternalModel
return internalModel?.coreModel
}
public getParameterRange(id: string) {
const coreModel = this.getCoreModel()
const index = coreModel?.getParameterIndex(id)
const min = coreModel?.getParameterMinimumValue(index)
const max = coreModel?.getParameterMaximumValue(index)
return {
min,
max,
}
}
public setParameterValue(id: string, value: number) {
const coreModel = this.getCoreModel()
return coreModel?.setParameterValueById?.(id, Number(value))
}
}
const live2d = new Live2d()
export default live2d

View File

@@ -1,24 +1,30 @@
import type { Cubism4InternalModel } from 'pixi-live2d-display'
import type { Sprite } from 'pixi.js'
import { convertFileSrc } from '@tauri-apps/api/core'
import { readDir, readTextFile } from '@tauri-apps/plugin-fs'
import { Cubism4ModelSettings, Live2DModel } from 'pixi-live2d-display'
import { Config, CubismSetting, Live2DSprite, LogLevel } from 'easy-live2d'
import { Application, Ticker } from 'pixi.js'
import { join } from './path'
Live2DModel.registerTicker(Ticker)
Config.MotionGroupIdle = 'Idle'
Config.MouseFollow = false
Config.CubismLoggingLevel = LogLevel.LogLevel_Off
class Live2d {
private app: Application | null = null
public model: Live2DModel | null = null
public model: Sprite | null = null
constructor() { }
private mount() {
private async initApp() {
if (this.app) return
const view = document.getElementById('live2dCanvas') as HTMLCanvasElement
this.app = new Application({
this.app = new Application()
await this.app.init({
view,
resizeTo: window,
backgroundAlpha: 0,
@@ -28,9 +34,7 @@ class Live2d {
}
public async load(path: string) {
if (!this.app) {
this.mount()
}
this.initApp()
this.destroy()
@@ -46,25 +50,27 @@ class Live2d {
const modelJSON = JSON.parse(await readTextFile(modelPath))
const modelSettings = new Cubism4ModelSettings({
...modelJSON,
url: convertFileSrc(modelPath),
const modelSetting = new CubismSetting({
modelJSON,
})
modelSettings.replaceFiles((file) => {
modelSetting.redirectPath(({ file }) => {
return convertFileSrc(join(path, file))
})
this.model = await Live2DModel.from(modelSettings)
this.model = new Live2DSprite({
modelSetting,
ticker: Ticker.shared,
})
this.app?.stage.addChild(this.model)
const { motions, expressions } = modelSettings
// const { motions, expressions } = modelSettings
return {
motions,
expressions,
}
// return {
// motions,
// expressions,
// }
}
public destroy() {
@@ -72,36 +78,36 @@ class Live2d {
}
public playMotion(group: string, index: number) {
return this.model?.motion(group, index)
// return this.model?.motion(group, index)
}
public playExpressions(index: number) {
return this.model?.expression(index)
// return this.model?.expression(index)
}
public getCoreModel() {
const internalModel = this.model?.internalModel as Cubism4InternalModel
// const internalModel = this.model?.internalModel as Cubism4InternalModel
return internalModel?.coreModel
// return internalModel?.coreModel
}
public getParameterRange(id: string) {
const coreModel = this.getCoreModel()
// const coreModel = this.getCoreModel()
const index = coreModel?.getParameterIndex(id)
const min = coreModel?.getParameterMinimumValue(index)
const max = coreModel?.getParameterMaximumValue(index)
// const index = coreModel?.getParameterIndex(id)
// const min = coreModel?.getParameterMinimumValue(index)
// const max = coreModel?.getParameterMaximumValue(index)
return {
min,
max,
min: 0,
max: 1,
}
}
public setParameterValue(id: string, value: number) {
const coreModel = this.getCoreModel()
// const coreModel = this.getCoreModel()
return coreModel?.setParameterValueById?.(id, Number(value))
// return coreModel?.setParameterValueById?.(id, Number(value))
}
}