refactor: migrating from react to vue3 and refactoring related code

This commit is contained in:
ayang
2025-03-28 18:40:08 +08:00
parent f27998e992
commit c52ef736c4
69 changed files with 5169 additions and 2276 deletions

15
.release-it.ts Normal file
View File

@@ -0,0 +1,15 @@
/* eslint-disable no-template-curly-in-string */
import type { Config } from 'release-it'
export default {
git: {
commitMessage: 'v${version}',
tagName: 'v${version}',
},
npm: {
publish: false,
},
hooks: {
'after:bump': 'tsx scripts/release.ts',
},
} satisfies Config

View File

@@ -1,8 +1,8 @@
{
"recommendations": [
"tauri-apps.tauri-vscode",
"rust-lang.rust-analyzer",
"biomejs.biome",
"antfu.unocss"
]
"recommendations": [
"tauri-apps.tauri-vscode",
"rust-lang.rust-analyzer",
"antfu.unocss",
"dbaeumer.vscode-eslint"
]
}

548
Cargo.lock generated
View File

@@ -62,6 +62,149 @@ version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
[[package]]
name = "async-broadcast"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
dependencies = [
"event-listener",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-channel"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a"
dependencies = [
"concurrent-queue",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-executor"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
"futures-lite",
"slab",
]
[[package]]
name = "async-fs"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a"
dependencies = [
"async-lock",
"blocking",
"futures-lite",
]
[[package]]
name = "async-io"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059"
dependencies = [
"async-lock",
"cfg-if",
"concurrent-queue",
"futures-io",
"futures-lite",
"parking",
"polling",
"rustix 0.38.44",
"slab",
"tracing",
"windows-sys 0.59.0",
]
[[package]]
name = "async-lock"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
dependencies = [
"event-listener",
"event-listener-strategy",
"pin-project-lite",
]
[[package]]
name = "async-process"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb"
dependencies = [
"async-channel",
"async-io",
"async-lock",
"async-signal",
"async-task",
"blocking",
"cfg-if",
"event-listener",
"futures-lite",
"rustix 0.38.44",
"tracing",
]
[[package]]
name = "async-recursion"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "async-signal"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3"
dependencies = [
"async-io",
"async-lock",
"atomic-waker",
"cfg-if",
"futures-core",
"futures-io",
"rustix 0.38.44",
"signal-hook-registry",
"slab",
"windows-sys 0.59.0",
]
[[package]]
name = "async-task"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
version = "0.1.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "atk"
version = "0.18.2"
@@ -85,6 +228,12 @@ dependencies = [
"system-deps",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.4.0"
@@ -166,6 +315,19 @@ dependencies = [
"objc2 0.6.0",
]
[[package]]
name = "blocking"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea"
dependencies = [
"async-channel",
"async-task",
"futures-io",
"futures-lite",
"piper",
]
[[package]]
name = "bongo-cat"
version = "0.0.0"
@@ -177,6 +339,7 @@ dependencies = [
"tauri-build",
"tauri-nspanel",
"tauri-plugin-custom-window",
"tauri-plugin-opener",
"tauri-plugin-os",
"tauri-plugin-process",
]
@@ -418,6 +581,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "convert_case"
version = "0.4.0"
@@ -789,6 +961,33 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
[[package]]
name = "endi"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
[[package]]
name = "enumflags2"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147"
dependencies = [
"enumflags2_derive",
"serde",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -815,6 +1014,33 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "event-listener"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
dependencies = [
"concurrent-queue",
"parking",
"pin-project-lite",
]
[[package]]
name = "event-listener-strategy"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
"event-listener",
"pin-project-lite",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fdeflate"
version = "0.3.7"
@@ -943,6 +1169,19 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-lite"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"parking",
"pin-project-lite",
]
[[package]]
name = "futures-macro"
version = "0.3.31"
@@ -1107,7 +1346,7 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed7131e57abbde63513e0e6636f76668a1ca9798dcae2df4e283cae9ee83859e"
dependencies = [
"rustix",
"rustix 1.0.3",
"windows-targets 0.52.6",
]
@@ -1323,6 +1562,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]]
name = "hex"
version = "0.4.3"
@@ -1649,6 +1894,25 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "is-docker"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3"
dependencies = [
"once_cell",
]
[[package]]
name = "is-wsl"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5"
dependencies = [
"is-docker",
"once_cell",
]
[[package]]
name = "itoa"
version = "0.4.8"
@@ -1818,6 +2082,12 @@ dependencies = [
"libc",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "linux-raw-sys"
version = "0.9.3"
@@ -1980,6 +2250,19 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "nix"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags 2.9.0",
"cfg-if",
"cfg_aliases",
"libc",
"memoffset",
]
[[package]]
name = "nodrop"
version = "0.1.14"
@@ -2278,12 +2561,34 @@ version = "1.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2806eaa3524762875e21c3dcd057bc4b7bfa01ce4da8d46be1cd43649e1cc6b"
[[package]]
name = "open"
version = "5.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95"
dependencies = [
"dunce",
"is-wsl",
"libc",
"pathdiff",
]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordered-stream"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
dependencies = [
"futures-core",
"pin-project-lite",
]
[[package]]
name = "os_info"
version = "3.10.0"
@@ -2320,6 +2625,12 @@ dependencies = [
"system-deps",
]
[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
version = "0.12.3"
@@ -2343,6 +2654,12 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "pathdiff"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
[[package]]
name = "percent-encoding"
version = "2.3.1"
@@ -2495,6 +2812,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "piper"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
dependencies = [
"atomic-waker",
"fastrand",
"futures-io",
]
[[package]]
name = "pkg-config"
version = "0.3.32"
@@ -2527,6 +2855,21 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "polling"
version = "3.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
dependencies = [
"cfg-if",
"concurrent-queue",
"hermit-abi",
"pin-project-lite",
"rustix 0.38.44",
"tracing",
"windows-sys 0.59.0",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
@@ -2844,6 +3187,19 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags 2.9.0",
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
]
[[package]]
name = "rustix"
version = "1.0.3"
@@ -2853,7 +3209,7 @@ dependencies = [
"bitflags 2.9.0",
"errno",
"libc",
"linux-raw-sys",
"linux-raw-sys 0.9.3",
"windows-sys 0.59.0",
]
@@ -3105,6 +3461,15 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
@@ -3202,6 +3567,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "string_cache"
version = "0.8.9"
@@ -3521,6 +3892,28 @@ dependencies = [
"tauri-plugin",
]
[[package]]
name = "tauri-plugin-opener"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fdc6cb608e04b7d2b6d1f21e9444ad49245f6d03465ba53323d692d1ceb1a30"
dependencies = [
"dunce",
"glob",
"objc2-app-kit",
"objc2-foundation 0.3.0",
"open",
"schemars",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"thiserror 2.0.12",
"url",
"windows",
"zbus",
]
[[package]]
name = "tauri-plugin-os"
version = "2.2.1"
@@ -3644,6 +4037,19 @@ dependencies = [
"toml",
]
[[package]]
name = "tempfile"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
dependencies = [
"fastrand",
"getrandom 0.3.2",
"once_cell",
"rustix 1.0.3",
"windows-sys 0.59.0",
]
[[package]]
name = "tendril"
version = "0.4.3"
@@ -3860,9 +4266,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "tracing-core"
version = "0.1.33"
@@ -3912,6 +4330,17 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]]
name = "uds_windows"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
dependencies = [
"memoffset",
"tempfile",
"winapi",
]
[[package]]
name = "unic-char-property"
version = "0.9.0"
@@ -4831,6 +5260,16 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "xdg-home"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "yoke"
version = "0.7.5"
@@ -4855,6 +5294,69 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zbus"
version = "5.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236"
dependencies = [
"async-broadcast",
"async-executor",
"async-fs",
"async-io",
"async-lock",
"async-process",
"async-recursion",
"async-task",
"async-trait",
"blocking",
"enumflags2",
"event-listener",
"futures-core",
"futures-lite",
"hex",
"nix",
"ordered-stream",
"serde",
"serde_repr",
"static_assertions",
"tracing",
"uds_windows",
"windows-sys 0.59.0",
"winnow 0.7.4",
"xdg-home",
"zbus_macros",
"zbus_names",
"zvariant",
]
[[package]]
name = "zbus_macros"
version = "5.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0"
dependencies = [
"proc-macro-crate 3.3.0",
"proc-macro2",
"quote",
"syn 2.0.100",
"zbus_names",
"zvariant",
"zvariant_utils",
]
[[package]]
name = "zbus_names"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97"
dependencies = [
"serde",
"static_assertions",
"winnow 0.7.4",
"zvariant",
]
[[package]]
name = "zerocopy"
version = "0.8.24"
@@ -4917,3 +5419,45 @@ dependencies = [
"quote",
"syn 2.0.100",
]
[[package]]
name = "zvariant"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2df9ee044893fcffbdc25de30546edef3e32341466811ca18421e3cd6c5a3ac"
dependencies = [
"endi",
"enumflags2",
"serde",
"static_assertions",
"winnow 0.7.4",
"zvariant_derive",
"zvariant_utils",
]
[[package]]
name = "zvariant_derive"
version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f"
dependencies = [
"proc-macro-crate 3.3.0",
"proc-macro2",
"quote",
"syn 2.0.100",
"zvariant_utils",
]
[[package]]
name = "zvariant_utils"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34"
dependencies = [
"proc-macro2",
"quote",
"serde",
"static_assertions",
"syn 2.0.100",
"winnow 0.7.4",
]

View File

@@ -1,14 +1,12 @@
[workspace]
resolver="2"
members = [
"src-tauri"
]
resolver = "2"
members = [ "src-tauri" ]
[workspace.dependencies]
tauri = "2"
tauri = "2"
serde = "1"
serde_json = "1"
fs_extra = "1"
tauri-plugin = { version = "2", features = ["build"] }
tauri-plugin = { version = "2", features = [ "build" ] }
tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" }
tauri-plugin-custom-window = { path = "./src-tauri/src/plugins/window" }
tauri-plugin-custom-window = { path = "./src-tauri/src/plugins/window" }

View File

@@ -15,9 +15,9 @@
/>
</a >
<a href="https://github.com/ayangweb/BongoCat/releases">
<img
<img
alt="Linux"
src="https://img.shields.io/badge/-Linux-yellow?style=flat-square&logo=linux&logoColor=white"
src="https://img.shields.io/badge/-Linux-yellow?style=flat-square&logo=linux&logoColor=white"
/>
</a>
</div>
@@ -36,7 +36,7 @@
<a href="https://github.com/ayangweb/BongoCat/releases">
<img
src="https://img.shields.io/github/downloads/ayangweb/BongoCat/total?style=flat-square"
/>
/>
</a >
</div>
</div>

View File

@@ -1,56 +0,0 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"files": {
"ignoreUnknown": false,
"ignore": []
},
"formatter": {
"enabled": true,
"indentStyle": "tab"
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noConsoleLog": "error"
},
"correctness": {
"noUnusedVariables": "error",
"noUnusedImports": "error",
"useExhaustiveDependencies": "off"
},
"nursery": {
"useSortedClasses": {
"level": "error",
"fix": "safe"
}
},
"style": {
"noUnusedTemplateLiteral": {
"level": "error",
"fix": "safe"
},
"useTemplate": {
"level": "error",
"fix": "safe"
},
"useSelfClosingElements": {
"level": "error",
"fix": "safe"
}
},
"a11y": {
"useAltText": "off"
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "double"
}
}
}

12
eslint.config.ts Normal file
View File

@@ -0,0 +1,12 @@
import antfu from '@antfu/eslint-config'
export default antfu({
formatters: true,
unocss: true,
rules: {
'antfu/if-newline': 'off',
'style/brace-style': ['error', '1tbs'],
'ts/no-use-before-define': 'off',
'unused-imports/no-unused-imports': 'error',
},
})

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
@@ -10,7 +10,7 @@
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -1,64 +1,66 @@
{
"name": "bongo-cat",
"private": true,
"author": {
"name": "ayangweb",
"email": "ayangweb@foxmail.com"
},
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "run-s build:icon dev:vite",
"build": "run-s build:*",
"dev:vite": "vite",
"build:vite": "vite build",
"build:icon": "tauri icon src-tauri/assets/logo.png",
"preview": "vite preview",
"tauri": "tauri",
"lint": "biome check --write src",
"preinstall": "npx only-allow pnpm",
"prepare": "simple-git-hooks",
"release": "release-it"
},
"dependencies": {
"@tauri-apps/api": "^2.4.0",
"@tauri-apps/plugin-os": "^2.2.1",
"@tauri-apps/plugin-process": "^2.2.0",
"@unocss/reset": "66.1.0-beta.7",
"pixi-live2d-display": "^0.4.0",
"pixi.js": "^6.5.10",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.4.0"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@commitlint/cli": "^19.8.0",
"@commitlint/config-conventional": "^19.8.0",
"@tauri-apps/cli": "^2.4.0",
"@types/node": "^22.13.14",
"@types/react": "^18.3.20",
"@types/react-dom": "^18.3.5",
"@unocss/preset-rem-to-px": "66.1.0-beta.7",
"@vitejs/plugin-react": "^4.3.4",
"lint-staged": "^15.5.0",
"npm-run-all": "^4.1.5",
"release-it": "^18.1.2",
"simple-git-hooks": "^2.12.1",
"typescript": "~5.6.3",
"unocss": "66.1.0-beta.7",
"vite": "^6.2.3"
},
"pnpm": {
"patchedDependencies": {
"pixi-live2d-display": "patches/pixi-live2d-display.patch"
}
},
"simple-git-hooks": {
"commit-msg": "npx --no-install commitlint -e",
"pre-commit": "npx lint-staged"
},
"lint-staged": {
"**.{ts,tsx,json}": ["biome check --apply --no-errors-on-unmatched"]
}
"name": "bongo-cat",
"type": "module",
"version": "0.0.0",
"private": true,
"author": {
"name": "ayangweb",
"email": "ayangweb@foxmail.com"
},
"scripts": {
"dev": "run-s build:icon dev:vite",
"build": "run-s build:*",
"dev:vite": "vite",
"build:vite": "vite build",
"build:icon": "tsx scripts/buildIcon.ts",
"preview": "vite preview",
"tauri": "tauri",
"lint": "eslint --fix",
"preinstall": "npx only-allow pnpm",
"prepare": "simple-git-hooks",
"release": "release-it"
},
"dependencies": {
"@tauri-apps/api": "^2.4.0",
"@tauri-apps/plugin-opener": "~2.2.6",
"@tauri-apps/plugin-os": "^2.2.1",
"@tauri-apps/plugin-process": "^2.2.0",
"@unocss/reset": "66.1.0-beta.7",
"pixi-live2d-display": "^0.4.0",
"pixi.js": "^6.5.10",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@antfu/eslint-config": "^4.11.0",
"@commitlint/cli": "^19.8.0",
"@commitlint/config-conventional": "^19.8.0",
"@tauri-apps/cli": "^2.4.0",
"@types/node": "^22.13.14",
"@unocss/eslint-plugin": "^66.0.0",
"@unocss/preset-rem-to-px": "66.1.0-beta.7",
"@vitejs/plugin-vue": "^5.2.3",
"eslint": "^9.23.0",
"eslint-plugin-format": "^1.0.1",
"lint-staged": "^15.5.0",
"npm-run-all": "^4.1.5",
"release-it": "^18.1.2",
"simple-git-hooks": "^2.12.1",
"tsx": "^4.19.3",
"typescript": "~5.6.3",
"unocss": "66.1.0-beta.7",
"vite": "^6.2.3"
},
"pnpm": {
"patchedDependencies": {
"pixi-live2d-display": "patches/pixi-live2d-display.patch"
}
},
"simple-git-hooks": {
"commit-msg": "npx --no-install commitlint -e",
"pre-commit": "npx lint-staged"
},
"lint-staged": {
"*": "eslint --fix"
}
}

2931
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

BIN
public/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,66 +1,69 @@
{
"Version": 3,
"FileReferences": {
"Moc": "demomodel2.moc3",
"Textures": [
"demomodel2.1024/texture_00.png",
"demomodel2.1024/texture_01.png",
"demomodel2.1024/texture_02.png"
],
"DisplayInfo": "demomodel2.cdi3.json",
"Expressions": [
{
"Name": "live2d_expression0.exp3.json",
"File": "live2d_expression0.exp3.json"
},
{
"Name": "live2d_expression1.exp3.json",
"File": "live2d_expression1.exp3.json"
},
{
"Name": "live2d_expression2.exp3.json",
"File": "live2d_expression2.exp3.json"
}
],
"Motions": {
"CAT_motion": [
{
"File": "live2d_motion1.motion3.json",
"Sound": "live2d_motion1.flac",
"FadeInTime": 0,
"FadeOutTime": 0
},
{
"File": "live2d_motion2.motion3.json",
"FadeInTime": 0,
"FadeOutTime": 0
}
],
"CAT_motion_lock": [
{
"File": "live2d_motion1.motion3.json",
"Sound": "live2d_motion1.flac",
"FadeInTime": 0,
"FadeOutTime": 0
},
{
"File": "live2d_motion2.motion3.json",
"FadeInTime": 0,
"FadeOutTime": 0
}
]
}
},
"Groups": [
{
"Target": "Parameter",
"Name": "EyeBlink",
"Ids": ["ParamEyeLOpen", "ParamEyeROpen"]
},
{
"Target": "Parameter",
"Name": "LipSync",
"Ids": []
}
]
"Version": 3,
"FileReferences": {
"Moc": "demomodel2.moc3",
"Textures": [
"demomodel2.1024/texture_00.png",
"demomodel2.1024/texture_01.png",
"demomodel2.1024/texture_02.png"
],
"DisplayInfo": "demomodel2.cdi3.json",
"Expressions": [
{
"Name": "live2d_expression0.exp3.json",
"File": "live2d_expression0.exp3.json"
},
{
"Name": "live2d_expression1.exp3.json",
"File": "live2d_expression1.exp3.json"
},
{
"Name": "live2d_expression2.exp3.json",
"File": "live2d_expression2.exp3.json"
}
],
"Motions": {
"CAT_motion": [
{
"File": "live2d_motion1.motion3.json",
"Sound": "live2d_motion1.flac",
"FadeInTime": 0,
"FadeOutTime": 0
},
{
"File": "live2d_motion2.motion3.json",
"FadeInTime": 0,
"FadeOutTime": 0
}
],
"CAT_motion_lock": [
{
"File": "live2d_motion1.motion3.json",
"Sound": "live2d_motion1.flac",
"FadeInTime": 0,
"FadeOutTime": 0
},
{
"File": "live2d_motion2.motion3.json",
"FadeInTime": 0,
"FadeOutTime": 0
}
]
}
},
"Groups": [
{
"Target": "Parameter",
"Name": "EyeBlink",
"Ids": [
"ParamEyeLOpen",
"ParamEyeROpen"
]
},
{
"Target": "Parameter",
"Name": "LipSync",
"Ids": []
}
]
}

View File

@@ -1,233 +1,233 @@
{
"Version": 3,
"Parameters": [
{
"Id": "ParamAngleX",
"GroupId": "",
"Name": "角度 X"
},
{
"Id": "ParamAngleY",
"GroupId": "",
"Name": "角度 Y"
},
{
"Id": "CatParamRightHandDown",
"GroupId": "",
"Name": "右手按下"
},
{
"Id": "CatParamLeftHandDown",
"GroupId": "",
"Name": "左手按下"
},
{
"Id": "ParamAngleZ",
"GroupId": "",
"Name": "角度 Z"
},
{
"Id": "ParamEyeLOpen",
"GroupId": "",
"Name": "左眼 开闭"
},
{
"Id": "ParamEyeLSmile",
"GroupId": "",
"Name": "左眼 微笑"
},
{
"Id": "ParamEyeROpen",
"GroupId": "",
"Name": "右眼"
},
{
"Id": "ParamEyeRSmile",
"GroupId": "",
"Name": "右眼 微笑"
},
{
"Id": "Param3",
"GroupId": "",
"Name": "挥手"
},
{
"Id": "Param",
"GroupId": "ParamGroup",
"Name": "开启闪电"
},
{
"Id": "Param2",
"GroupId": "ParamGroup",
"Name": "闪电划过"
},
{
"Id": "Param4",
"GroupId": "ParamGroup2",
"Name": "表情:thuglife"
},
{
"Id": "Param5",
"GroupId": "ParamGroup2",
"Name": "表情:升天"
},
{
"Id": "ParamEyeBallX",
"GroupId": "",
"Name": "眼球 X"
},
{
"Id": "ParamEyeBallY",
"GroupId": "",
"Name": "眼球 Y"
},
{
"Id": "ParamBrowLY",
"GroupId": "",
"Name": "左眉上下"
},
{
"Id": "ParamBrowRY",
"GroupId": "",
"Name": "右眉 上下"
},
{
"Id": "ParamBrowLX",
"GroupId": "",
"Name": "左眉 左右"
},
{
"Id": "ParamBrowRX",
"GroupId": "",
"Name": "右眉 左右"
},
{
"Id": "ParamBrowLAngle",
"GroupId": "",
"Name": "左眉 角度"
},
{
"Id": "ParamBrowRAngle",
"GroupId": "",
"Name": "右眉 角度"
},
{
"Id": "ParamBrowLForm",
"GroupId": "",
"Name": "左眉 変形"
},
{
"Id": "ParamBrowRForm",
"GroupId": "",
"Name": "右眉 変形"
},
{
"Id": "ParamMouthForm",
"GroupId": "",
"Name": "嘴部 变形"
},
{
"Id": "ParamMouthOpenY",
"GroupId": "",
"Name": "嘴巴 张开和闭合"
},
{
"Id": "ParamCheek",
"GroupId": "",
"Name": "脸颊"
},
{
"Id": "ParamBodyAngleX",
"GroupId": "",
"Name": "身体旋转 X"
},
{
"Id": "ParamBodyAngleY",
"GroupId": "",
"Name": "身体旋转 Y"
},
{
"Id": "ParamBodyAngleZ",
"GroupId": "",
"Name": "身体旋转 Z"
},
{
"Id": "ParamBreath",
"GroupId": "",
"Name": "呼吸"
},
{
"Id": "ParamHairFront",
"GroupId": "",
"Name": "摇动 前发"
},
{
"Id": "ParamHairSide",
"GroupId": "",
"Name": "摇动 侧发"
},
{
"Id": "ParamHairBack",
"GroupId": "",
"Name": "摇动 后发"
}
],
"ParameterGroups": [
{
"Id": "ParamGroup",
"GroupId": "",
"Name": "闪电"
},
{
"Id": "ParamGroup2",
"GroupId": "",
"Name": "表情"
}
],
"Parts": [
{
"Id": "Part11",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part7",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part3",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part2",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part10",
"Name": "天使环"
},
{
"Id": "Part5",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "PartSketch0",
"Name": "[ 参考图 ]"
},
{
"Id": "Part8",
"Name": "thug life"
},
{
"Id": "Part6",
"Name": "闪电"
},
{
"Id": "Part4",
"Name": "闪电"
}
]
"Version": 3,
"Parameters": [
{
"Id": "ParamAngleX",
"GroupId": "",
"Name": "角度 X"
},
{
"Id": "ParamAngleY",
"GroupId": "",
"Name": "角度 Y"
},
{
"Id": "CatParamRightHandDown",
"GroupId": "",
"Name": "右手按下"
},
{
"Id": "CatParamLeftHandDown",
"GroupId": "",
"Name": "左手按下"
},
{
"Id": "ParamAngleZ",
"GroupId": "",
"Name": "角度 Z"
},
{
"Id": "ParamEyeLOpen",
"GroupId": "",
"Name": "左眼 开闭"
},
{
"Id": "ParamEyeLSmile",
"GroupId": "",
"Name": "左眼 微笑"
},
{
"Id": "ParamEyeROpen",
"GroupId": "",
"Name": "右眼"
},
{
"Id": "ParamEyeRSmile",
"GroupId": "",
"Name": "右眼 微笑"
},
{
"Id": "Param3",
"GroupId": "",
"Name": "挥手"
},
{
"Id": "Param",
"GroupId": "ParamGroup",
"Name": "开启闪电"
},
{
"Id": "Param2",
"GroupId": "ParamGroup",
"Name": "闪电划过"
},
{
"Id": "Param4",
"GroupId": "ParamGroup2",
"Name": "表情:thuglife"
},
{
"Id": "Param5",
"GroupId": "ParamGroup2",
"Name": "表情:升天"
},
{
"Id": "ParamEyeBallX",
"GroupId": "",
"Name": "眼球 X"
},
{
"Id": "ParamEyeBallY",
"GroupId": "",
"Name": "眼球 Y"
},
{
"Id": "ParamBrowLY",
"GroupId": "",
"Name": "左眉上下"
},
{
"Id": "ParamBrowRY",
"GroupId": "",
"Name": "右眉 上下"
},
{
"Id": "ParamBrowLX",
"GroupId": "",
"Name": "左眉 左右"
},
{
"Id": "ParamBrowRX",
"GroupId": "",
"Name": "右眉 左右"
},
{
"Id": "ParamBrowLAngle",
"GroupId": "",
"Name": "左眉 角度"
},
{
"Id": "ParamBrowRAngle",
"GroupId": "",
"Name": "右眉 角度"
},
{
"Id": "ParamBrowLForm",
"GroupId": "",
"Name": "左眉 変形"
},
{
"Id": "ParamBrowRForm",
"GroupId": "",
"Name": "右眉 変形"
},
{
"Id": "ParamMouthForm",
"GroupId": "",
"Name": "嘴部 变形"
},
{
"Id": "ParamMouthOpenY",
"GroupId": "",
"Name": "嘴巴 张开和闭合"
},
{
"Id": "ParamCheek",
"GroupId": "",
"Name": "脸颊"
},
{
"Id": "ParamBodyAngleX",
"GroupId": "",
"Name": "身体旋转 X"
},
{
"Id": "ParamBodyAngleY",
"GroupId": "",
"Name": "身体旋转 Y"
},
{
"Id": "ParamBodyAngleZ",
"GroupId": "",
"Name": "身体旋转 Z"
},
{
"Id": "ParamBreath",
"GroupId": "",
"Name": "呼吸"
},
{
"Id": "ParamHairFront",
"GroupId": "",
"Name": "摇动 前发"
},
{
"Id": "ParamHairSide",
"GroupId": "",
"Name": "摇动 侧发"
},
{
"Id": "ParamHairBack",
"GroupId": "",
"Name": "摇动 后发"
}
],
"ParameterGroups": [
{
"Id": "ParamGroup",
"GroupId": "",
"Name": "闪电"
},
{
"Id": "ParamGroup2",
"GroupId": "",
"Name": "表情"
}
],
"Parts": [
{
"Id": "Part11",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part7",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part3",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part2",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part10",
"Name": "天使环"
},
{
"Id": "Part5",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "PartSketch0",
"Name": "[ 参考图 ]"
},
{
"Id": "Part8",
"Name": "thug life"
},
{
"Id": "Part6",
"Name": "闪电"
},
{
"Id": "Part4",
"Name": "闪电"
}
]
}

View File

@@ -1,15 +1,15 @@
{
"Type": "Live2D Expression",
"Parameters": [
{
"Id": "ParamEyeLOpen",
"Value": 0.321,
"Blend": "Multiply"
},
{
"Id": "ParamEyeROpen",
"Value": 0.313,
"Blend": "Multiply"
}
]
"Type": "Live2D Expression",
"Parameters": [
{
"Id": "ParamEyeLOpen",
"Value": 0.321,
"Blend": "Multiply"
},
{
"Id": "ParamEyeROpen",
"Value": 0.313,
"Blend": "Multiply"
}
]
}

View File

@@ -1,10 +1,10 @@
{
"Type": "Live2D Expression",
"Parameters": [
{
"Id": "ParamEyeLOpen",
"Value": -1,
"Blend": "Add"
}
]
"Type": "Live2D Expression",
"Parameters": [
{
"Id": "ParamEyeLOpen",
"Value": -1,
"Blend": "Add"
}
]
}

View File

@@ -1,4 +1,4 @@
{
"Type": "Live2D Expression",
"Parameters": []
"Type": "Live2D Expression",
"Parameters": []
}

View File

@@ -1,11 +1,11 @@
{
"Type": "Live2D Expression",
"FadeInTime": 0.8,
"Parameters": [
{
"Id": "Param4",
"Value": 1,
"Blend": "Add"
}
]
"Type": "Live2D Expression",
"FadeInTime": 0.8,
"Parameters": [
{
"Id": "Param4",
"Value": 1,
"Blend": "Add"
}
]
}

View File

@@ -1,11 +1,11 @@
{
"Type": "Live2D Expression",
"FadeInTime": 0.5,
"Parameters": [
{
"Id": "Param5",
"Value": 1,
"Blend": "Add"
}
]
"Type": "Live2D Expression",
"FadeInTime": 0.5,
"Parameters": [
{
"Id": "Param5",
"Value": 1,
"Blend": "Add"
}
]
}

View File

@@ -1,32 +1,76 @@
{
"Version": 3,
"Meta": {
"Duration": 1.633,
"Fps": 30.0,
"Loop": true,
"AreBeziersRestricted": false,
"CurveCount": 2,
"TotalSegmentCount": 8,
"TotalPointCount": 20,
"UserDataCount": 0,
"TotalUserDataSize": 0
},
"Curves": [
{
"Target": "Parameter",
"Id": "Param",
"Segments": [
0, 0, 1, 0.033, 0, 0.067, 1, 0.1, 1, 1, 0.411, 1, 0.722, 1, 1.033, 1, 1,
1.189, 1, 1.344, 0, 1.5, 0, 0, 1.633, 0
]
},
{
"Target": "Parameter",
"Id": "Param2",
"Segments": [
0, 0, 0, 0.067, 0, 1, 0.1, 0, 0.133, 0.142, 0.167, 0.2, 1, 0.489, 0.764,
0.811, 1, 1.133, 1, 0, 1.633, 1
]
}
]
"Version": 3,
"Meta": {
"Duration": 1.633,
"Fps": 30.0,
"Loop": true,
"AreBeziersRestricted": false,
"CurveCount": 2,
"TotalSegmentCount": 8,
"TotalPointCount": 20,
"UserDataCount": 0,
"TotalUserDataSize": 0
},
"Curves": [
{
"Target": "Parameter",
"Id": "Param",
"Segments": [
0,
0,
1,
0.033,
0,
0.067,
1,
0.1,
1,
1,
0.411,
1,
0.722,
1,
1.033,
1,
1,
1.189,
1,
1.344,
0,
1.5,
0,
0,
1.633,
0
]
},
{
"Target": "Parameter",
"Id": "Param2",
"Segments": [
0,
0,
0,
0.067,
0,
1,
0.1,
0,
0.133,
0.142,
0.167,
0.2,
1,
0.489,
0.764,
0.811,
1,
1.133,
1,
0,
1.633,
1
]
}
]
}

View File

@@ -1,30 +1,77 @@
{
"Version": 3,
"Meta": {
"Duration": 2.333,
"Fps": 30.0,
"Loop": true,
"AreBeziersRestricted": true,
"CurveCount": 2,
"TotalSegmentCount": 7,
"TotalPointCount": 21,
"UserDataCount": 0,
"TotalUserDataSize": 0
},
"Curves": [
{
"Target": "Parameter",
"Id": "CatParamLeftHandDown",
"Segments": [0, 0, 0, 2.333, 0]
},
{
"Target": "Parameter",
"Id": "Param3",
"Segments": [
0, 0, 1, 0.133, 0, 0.267, 30, 0.4, 30, 1, 0.522, 30, 0.644, 0, 0.767, 0,
1, 0.9, 0, 1.033, 30, 1.167, 30, 1, 1.3, 30, 1.433, 0, 1.567, 0, 1, 1.7,
0, 1.833, 30, 1.967, 30, 1, 2.089, 30, 2.211, 0, 2.333, 0
]
}
]
"Version": 3,
"Meta": {
"Duration": 2.333,
"Fps": 30.0,
"Loop": true,
"AreBeziersRestricted": true,
"CurveCount": 2,
"TotalSegmentCount": 7,
"TotalPointCount": 21,
"UserDataCount": 0,
"TotalUserDataSize": 0
},
"Curves": [
{
"Target": "Parameter",
"Id": "CatParamLeftHandDown",
"Segments": [
0,
0,
0,
2.333,
0
]
},
{
"Target": "Parameter",
"Id": "Param3",
"Segments": [
0,
0,
1,
0.133,
0,
0.267,
30,
0.4,
30,
1,
0.522,
30,
0.644,
0,
0.767,
0,
1,
0.9,
0,
1.033,
30,
1.167,
30,
1,
1.3,
30,
1.433,
0,
1.567,
0,
1,
1.7,
0,
1.833,
30,
1.967,
30,
1,
2.089,
30,
2.211,
0,
2.333,
0
]
}
]
}

View File

@@ -1,67 +1,70 @@
{
"Version": 3,
"FileReferences": {
"Moc": "demomodel.moc3",
"Textures": [
"demomodel.1024/texture_00.png",
"demomodel.1024/texture_01.png",
"demomodel.1024/texture_02.png"
],
"DisplayInfo": "demomodel.cdi3.json",
"Expressions": [
{
"Name": "live2d_expression0.exp3.json",
"File": "live2d_expression0.exp3.json"
},
{
"Name": "live2d_expression1.exp3.json",
"File": "live2d_expression1.exp3.json"
},
{
"Name": "live2d_expression2.exp3.json",
"File": "live2d_expression2.exp3.json"
}
],
"Motions": {
"CAT_motion": [
{
"File": "live2d_motion1.motion3.json",
"Sound": "live2d_motion1.flac",
"FadeInTime": 0,
"FadeOutTime": 0
},
{
"File": "live2d_motion2.motion3.json",
"FadeInTime": 0,
"FadeOutTime": 0
}
],
"CAT_motion_lock": [
{
"File": "live2d_motion1.motion3.json",
"Sound": "live2d_motion1.flac",
"FadeInTime": 0,
"FadeOutTime": 0
},
{
"File": "live2d_motion2.motion3.json",
"FadeInTime": 0,
"FadeOutTime": 0
}
]
}
},
"Groups": [
{
"Target": "Parameter",
"Name": "EyeBlink",
"Ids": ["ParamEyeLOpen", "ParamEyeROpen"]
},
{
"Target": "Parameter",
"Name": "LipSync",
"Ids": []
}
],
"HitAreas": []
"Version": 3,
"FileReferences": {
"Moc": "demomodel.moc3",
"Textures": [
"demomodel.1024/texture_00.png",
"demomodel.1024/texture_01.png",
"demomodel.1024/texture_02.png"
],
"DisplayInfo": "demomodel.cdi3.json",
"Expressions": [
{
"Name": "live2d_expression0.exp3.json",
"File": "live2d_expression0.exp3.json"
},
{
"Name": "live2d_expression1.exp3.json",
"File": "live2d_expression1.exp3.json"
},
{
"Name": "live2d_expression2.exp3.json",
"File": "live2d_expression2.exp3.json"
}
],
"Motions": {
"CAT_motion": [
{
"File": "live2d_motion1.motion3.json",
"Sound": "live2d_motion1.flac",
"FadeInTime": 0,
"FadeOutTime": 0
},
{
"File": "live2d_motion2.motion3.json",
"FadeInTime": 0,
"FadeOutTime": 0
}
],
"CAT_motion_lock": [
{
"File": "live2d_motion1.motion3.json",
"Sound": "live2d_motion1.flac",
"FadeInTime": 0,
"FadeOutTime": 0
},
{
"File": "live2d_motion2.motion3.json",
"FadeInTime": 0,
"FadeOutTime": 0
}
]
}
},
"Groups": [
{
"Target": "Parameter",
"Name": "EyeBlink",
"Ids": [
"ParamEyeLOpen",
"ParamEyeROpen"
]
},
{
"Target": "Parameter",
"Name": "LipSync",
"Ids": []
}
],
"HitAreas": []
}

View File

@@ -1,244 +1,244 @@
{
"Version": 3,
"Parameters": [
{
"Id": "ParamAngleX",
"GroupId": "",
"Name": "角度 X"
},
{
"Id": "ParamAngleY",
"GroupId": "",
"Name": "角度 Y"
},
{
"Id": "ParamMouseX",
"GroupId": "",
"Name": "鼠标X"
},
{
"Id": "ParamMouseY",
"GroupId": "",
"Name": "鼠标Y"
},
{
"Id": "ParamMouseLeftDown",
"GroupId": "",
"Name": "鼠标左键按下"
},
{
"Id": "ParamMouseRightDown",
"GroupId": "",
"Name": "鼠标右键按下"
},
{
"Id": "CatParamLeftHandDown",
"GroupId": "",
"Name": "键盘按下"
},
{
"Id": "ParamAngleZ",
"GroupId": "",
"Name": "角度 Z"
},
{
"Id": "ParamEyeLOpen",
"GroupId": "",
"Name": "左眼 开闭"
},
{
"Id": "ParamEyeLSmile",
"GroupId": "",
"Name": "左眼 微笑"
},
{
"Id": "ParamEyeROpen",
"GroupId": "",
"Name": "右眼"
},
{
"Id": "ParamEyeRSmile",
"GroupId": "",
"Name": "右眼 微笑"
},
{
"Id": "Param3",
"GroupId": "",
"Name": "挥手"
},
{
"Id": "Param",
"GroupId": "ParamGroup",
"Name": "开启闪电"
},
{
"Id": "Param2",
"GroupId": "ParamGroup",
"Name": "闪电划过"
},
{
"Id": "Param4",
"GroupId": "ParamGroup2",
"Name": "表情:thuglife"
},
{
"Id": "Param5",
"GroupId": "ParamGroup2",
"Name": "表情:升天"
},
{
"Id": "ParamEyeBallX",
"GroupId": "",
"Name": "眼球 X"
},
{
"Id": "ParamEyeBallY",
"GroupId": "",
"Name": "眼球 Y"
},
{
"Id": "ParamBrowLY",
"GroupId": "",
"Name": "左眉上下"
},
{
"Id": "ParamBrowRY",
"GroupId": "",
"Name": "右眉 上下"
},
{
"Id": "ParamBrowLX",
"GroupId": "",
"Name": "左眉 左右"
},
{
"Id": "ParamBrowRX",
"GroupId": "",
"Name": "右眉 左右"
},
{
"Id": "ParamBrowLAngle",
"GroupId": "",
"Name": "左眉 角度"
},
{
"Id": "ParamBrowRAngle",
"GroupId": "",
"Name": "右眉 角度"
},
{
"Id": "ParamBrowLForm",
"GroupId": "",
"Name": "左眉 変形"
},
{
"Id": "ParamBrowRForm",
"GroupId": "",
"Name": "右眉 変形"
},
{
"Id": "ParamMouthForm",
"GroupId": "",
"Name": "嘴部 变形"
},
{
"Id": "ParamMouthOpenY",
"GroupId": "",
"Name": "嘴巴 张开和闭合"
},
{
"Id": "ParamCheek",
"GroupId": "",
"Name": "脸颊"
},
{
"Id": "ParamBodyAngleX",
"GroupId": "",
"Name": "身体旋转 X"
},
{
"Id": "ParamBodyAngleY",
"GroupId": "",
"Name": "身体旋转 Y"
},
{
"Id": "ParamBodyAngleZ",
"GroupId": "",
"Name": "身体旋转 Z"
},
{
"Id": "ParamBreath",
"GroupId": "",
"Name": "呼吸"
},
{
"Id": "ParamHairFront",
"GroupId": "",
"Name": "摇动 前发"
},
{
"Id": "ParamHairSide",
"GroupId": "",
"Name": "摇动 侧发"
},
{
"Id": "ParamHairBack",
"GroupId": "",
"Name": "摇动 后发"
}
],
"ParameterGroups": [
{
"Id": "ParamGroup",
"GroupId": "",
"Name": "闪电"
},
{
"Id": "ParamGroup2",
"GroupId": "",
"Name": "表情"
}
],
"Parts": [
{
"Id": "Part11",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part7",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part3",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part2",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part10",
"Name": "天使环"
},
{
"Id": "Part5",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part8",
"Name": "thug life"
},
{
"Id": "Part6",
"Name": "闪电"
},
{
"Id": "Part4",
"Name": "闪电"
}
]
"Version": 3,
"Parameters": [
{
"Id": "ParamAngleX",
"GroupId": "",
"Name": "角度 X"
},
{
"Id": "ParamAngleY",
"GroupId": "",
"Name": "角度 Y"
},
{
"Id": "ParamMouseX",
"GroupId": "",
"Name": "鼠标X"
},
{
"Id": "ParamMouseY",
"GroupId": "",
"Name": "鼠标Y"
},
{
"Id": "ParamMouseLeftDown",
"GroupId": "",
"Name": "鼠标左键按下"
},
{
"Id": "ParamMouseRightDown",
"GroupId": "",
"Name": "鼠标右键按下"
},
{
"Id": "CatParamLeftHandDown",
"GroupId": "",
"Name": "键盘按下"
},
{
"Id": "ParamAngleZ",
"GroupId": "",
"Name": "角度 Z"
},
{
"Id": "ParamEyeLOpen",
"GroupId": "",
"Name": "左眼 开闭"
},
{
"Id": "ParamEyeLSmile",
"GroupId": "",
"Name": "左眼 微笑"
},
{
"Id": "ParamEyeROpen",
"GroupId": "",
"Name": "右眼"
},
{
"Id": "ParamEyeRSmile",
"GroupId": "",
"Name": "右眼 微笑"
},
{
"Id": "Param3",
"GroupId": "",
"Name": "挥手"
},
{
"Id": "Param",
"GroupId": "ParamGroup",
"Name": "开启闪电"
},
{
"Id": "Param2",
"GroupId": "ParamGroup",
"Name": "闪电划过"
},
{
"Id": "Param4",
"GroupId": "ParamGroup2",
"Name": "表情:thuglife"
},
{
"Id": "Param5",
"GroupId": "ParamGroup2",
"Name": "表情:升天"
},
{
"Id": "ParamEyeBallX",
"GroupId": "",
"Name": "眼球 X"
},
{
"Id": "ParamEyeBallY",
"GroupId": "",
"Name": "眼球 Y"
},
{
"Id": "ParamBrowLY",
"GroupId": "",
"Name": "左眉上下"
},
{
"Id": "ParamBrowRY",
"GroupId": "",
"Name": "右眉 上下"
},
{
"Id": "ParamBrowLX",
"GroupId": "",
"Name": "左眉 左右"
},
{
"Id": "ParamBrowRX",
"GroupId": "",
"Name": "右眉 左右"
},
{
"Id": "ParamBrowLAngle",
"GroupId": "",
"Name": "左眉 角度"
},
{
"Id": "ParamBrowRAngle",
"GroupId": "",
"Name": "右眉 角度"
},
{
"Id": "ParamBrowLForm",
"GroupId": "",
"Name": "左眉 変形"
},
{
"Id": "ParamBrowRForm",
"GroupId": "",
"Name": "右眉 変形"
},
{
"Id": "ParamMouthForm",
"GroupId": "",
"Name": "嘴部 变形"
},
{
"Id": "ParamMouthOpenY",
"GroupId": "",
"Name": "嘴巴 张开和闭合"
},
{
"Id": "ParamCheek",
"GroupId": "",
"Name": "脸颊"
},
{
"Id": "ParamBodyAngleX",
"GroupId": "",
"Name": "身体旋转 X"
},
{
"Id": "ParamBodyAngleY",
"GroupId": "",
"Name": "身体旋转 Y"
},
{
"Id": "ParamBodyAngleZ",
"GroupId": "",
"Name": "身体旋转 Z"
},
{
"Id": "ParamBreath",
"GroupId": "",
"Name": "呼吸"
},
{
"Id": "ParamHairFront",
"GroupId": "",
"Name": "摇动 前发"
},
{
"Id": "ParamHairSide",
"GroupId": "",
"Name": "摇动 侧发"
},
{
"Id": "ParamHairBack",
"GroupId": "",
"Name": "摇动 后发"
}
],
"ParameterGroups": [
{
"Id": "ParamGroup",
"GroupId": "",
"Name": "闪电"
},
{
"Id": "ParamGroup2",
"GroupId": "",
"Name": "表情"
}
],
"Parts": [
{
"Id": "Part11",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part7",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part3",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part2",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part10",
"Name": "天使环"
},
{
"Id": "Part5",
"Name": "demomodel.psd(未找到对应图层)"
},
{
"Id": "Part8",
"Name": "thug life"
},
{
"Id": "Part6",
"Name": "闪电"
},
{
"Id": "Part4",
"Name": "闪电"
}
]
}

View File

@@ -1,15 +1,15 @@
{
"Type": "Live2D Expression",
"Parameters": [
{
"Id": "ParamEyeLOpen",
"Value": 0.321,
"Blend": "Multiply"
},
{
"Id": "ParamEyeROpen",
"Value": 0.313,
"Blend": "Multiply"
}
]
"Type": "Live2D Expression",
"Parameters": [
{
"Id": "ParamEyeLOpen",
"Value": 0.321,
"Blend": "Multiply"
},
{
"Id": "ParamEyeROpen",
"Value": 0.313,
"Blend": "Multiply"
}
]
}

View File

@@ -1,10 +1,10 @@
{
"Type": "Live2D Expression",
"Parameters": [
{
"Id": "ParamEyeLOpen",
"Value": -1,
"Blend": "Add"
}
]
"Type": "Live2D Expression",
"Parameters": [
{
"Id": "ParamEyeLOpen",
"Value": -1,
"Blend": "Add"
}
]
}

View File

@@ -1,4 +1,4 @@
{
"Type": "Live2D Expression",
"Parameters": []
"Type": "Live2D Expression",
"Parameters": []
}

View File

@@ -1,11 +1,11 @@
{
"Type": "Live2D Expression",
"FadeInTime": 0.8,
"Parameters": [
{
"Id": "Param4",
"Value": 1,
"Blend": "Add"
}
]
"Type": "Live2D Expression",
"FadeInTime": 0.8,
"Parameters": [
{
"Id": "Param4",
"Value": 1,
"Blend": "Add"
}
]
}

View File

@@ -1,11 +1,11 @@
{
"Type": "Live2D Expression",
"FadeInTime": 0.5,
"Parameters": [
{
"Id": "Param5",
"Value": 1,
"Blend": "Add"
}
]
"Type": "Live2D Expression",
"FadeInTime": 0.5,
"Parameters": [
{
"Id": "Param5",
"Value": 1,
"Blend": "Add"
}
]
}

View File

@@ -1,32 +1,76 @@
{
"Version": 3,
"Meta": {
"Duration": 1.633,
"Fps": 30.0,
"Loop": true,
"AreBeziersRestricted": false,
"CurveCount": 2,
"TotalSegmentCount": 8,
"TotalPointCount": 20,
"UserDataCount": 0,
"TotalUserDataSize": 0
},
"Curves": [
{
"Target": "Parameter",
"Id": "Param",
"Segments": [
0, 0, 1, 0.033, 0, 0.067, 1, 0.1, 1, 1, 0.411, 1, 0.722, 1, 1.033, 1, 1,
1.189, 1, 1.344, 0, 1.5, 0, 0, 1.633, 0
]
},
{
"Target": "Parameter",
"Id": "Param2",
"Segments": [
0, 0, 0, 0.067, 0, 1, 0.1, 0, 0.133, 0.142, 0.167, 0.2, 1, 0.489, 0.764,
0.811, 1, 1.133, 1, 0, 1.633, 1
]
}
]
"Version": 3,
"Meta": {
"Duration": 1.633,
"Fps": 30.0,
"Loop": true,
"AreBeziersRestricted": false,
"CurveCount": 2,
"TotalSegmentCount": 8,
"TotalPointCount": 20,
"UserDataCount": 0,
"TotalUserDataSize": 0
},
"Curves": [
{
"Target": "Parameter",
"Id": "Param",
"Segments": [
0,
0,
1,
0.033,
0,
0.067,
1,
0.1,
1,
1,
0.411,
1,
0.722,
1,
1.033,
1,
1,
1.189,
1,
1.344,
0,
1.5,
0,
0,
1.633,
0
]
},
{
"Target": "Parameter",
"Id": "Param2",
"Segments": [
0,
0,
0,
0.067,
0,
1,
0.1,
0,
0.133,
0.142,
0.167,
0.2,
1,
0.489,
0.764,
0.811,
1,
1.133,
1,
0,
1.633,
1
]
}
]
}

View File

@@ -1,30 +1,77 @@
{
"Version": 3,
"Meta": {
"Duration": 2.333,
"Fps": 30.0,
"Loop": true,
"AreBeziersRestricted": true,
"CurveCount": 2,
"TotalSegmentCount": 7,
"TotalPointCount": 21,
"UserDataCount": 0,
"TotalUserDataSize": 0
},
"Curves": [
{
"Target": "Parameter",
"Id": "CatParamLeftHandDown",
"Segments": [0, 0, 0, 2.333, 0]
},
{
"Target": "Parameter",
"Id": "Param3",
"Segments": [
0, 0, 1, 0.133, 0, 0.267, 30, 0.4, 30, 1, 0.522, 30, 0.644, 0, 0.767, 0,
1, 0.9, 0, 1.033, 30, 1.167, 30, 1, 1.3, 30, 1.433, 0, 1.567, 0, 1, 1.7,
0, 1.833, 30, 1.967, 30, 1, 2.089, 30, 2.211, 0, 2.333, 0
]
}
]
"Version": 3,
"Meta": {
"Duration": 2.333,
"Fps": 30.0,
"Loop": true,
"AreBeziersRestricted": true,
"CurveCount": 2,
"TotalSegmentCount": 7,
"TotalPointCount": 21,
"UserDataCount": 0,
"TotalUserDataSize": 0
},
"Curves": [
{
"Target": "Parameter",
"Id": "CatParamLeftHandDown",
"Segments": [
0,
0,
0,
2.333,
0
]
},
{
"Target": "Parameter",
"Id": "Param3",
"Segments": [
0,
0,
1,
0.133,
0,
0.267,
30,
0.4,
30,
1,
0.522,
30,
0.644,
0,
0.767,
0,
1,
0.9,
0,
1.033,
30,
1.167,
30,
1,
1.3,
30,
1.433,
0,
1.567,
0,
1,
1.7,
0,
1.833,
30,
1.967,
30,
1,
2.089,
30,
2.211,
0,
2.333,
0
]
}
]
}

12
scripts/buildIcon.ts Normal file
View File

@@ -0,0 +1,12 @@
import { execSync } from 'node:child_process'
import { env, platform } from 'node:process'
(() => {
const isMac = env.PLATFORM?.startsWith('macos') ?? platform === 'darwin'
const logoName = isMac ? 'logo-mac' : 'logo'
const command = `tauri icon src-tauri/assets/${logoName}.png`
execSync(command, { stdio: 'inherit' })
})()

23
scripts/release.ts Normal file
View File

@@ -0,0 +1,23 @@
import { readFileSync, writeFileSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { name, version } from '../package.json'
const __dirname = dirname(fileURLToPath(import.meta.url));
(() => {
const tomlPath = resolve(__dirname, '..', 'src-tauri', 'Cargo.toml')
const lockPath = resolve(__dirname, '..', 'Cargo.lock')
for (const path of [tomlPath, lockPath]) {
let content = readFileSync(path, 'utf-8')
const regexp = new RegExp(
`(name\\s*=\\s*"${name}"\\s*version\\s*=\\s*)"(\\d+\\.\\d+\\.\\d+(-\\w+\\.\\d+)?)"`,
)
content = content.replace(regexp, `$1"${version}"`)
writeFileSync(path, content)
}
})()

View File

@@ -2,7 +2,7 @@
name = "bongo-cat"
version = "0.0.0"
description = "A Tauri App"
authors = ["ayangweb"]
authors = [ "ayangweb" ]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -12,19 +12,31 @@ edition = "2021"
# to make the lib name unique and wouldn't conflict with the bin name.
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
name = "bongo_cat_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
crate-type = [
"staticlib",
"cdylib",
"rlib"
]
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { workspace = true, features = ["macos-private-api", "tray-icon", "image-png"] }
serde = { workspace = true, features = ["derive"] }
tauri = { workspace = true, features = [
"macos-private-api",
"tray-icon",
"image-png"
] }
serde = { workspace = true, features = [ "derive" ] }
serde_json.workspace = true
tauri-plugin-custom-window.workspace = true
rdev = "0.5"
tauri-plugin-os = "2"
tauri-plugin-process = "2"
tauri-plugin-opener = "2"
[target."cfg(target_os = \"macos\")".dependencies]
tauri-nspanel.workspace = true

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -1,14 +1,17 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["*"],
"permissions": [
"core:default",
"core:window:allow-start-dragging",
"core:window:allow-set-size",
"custom-window:default",
"os:default",
"process:default"
]
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": [
"*"
],
"permissions": [
"core:default",
"core:window:allow-start-dragging",
"core:window:allow-set-size",
"custom-window:default",
"os:default",
"process:default",
"opener:default"
]
}

View File

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

View File

@@ -23,6 +23,7 @@ pub fn run() {
.plugin(tauri_plugin_custom_window::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_opener::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -9,10 +9,11 @@ links = "tauri-plugin-custom-window"
[dependencies]
tauri.workspace = true
serde.workspace = true
[build-dependencies]
tauri-plugin.workspace = true
[target."cfg(target_os = \"macos\")".dependencies]
tauri-nspanel.workspace = true
tauri-nspanel.workspace = true

View File

@@ -2,4 +2,7 @@
[default]
description = "Default permissions for the plugin"
permissions = ["allow-show-window", "allow-hide-window"]
permissions = [
"allow-show-window",
"allow-hide-window"
]

View File

@@ -1,286 +1,325 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PermissionFile",
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
"type": "object",
"properties": {
"default": {
"description": "The default permission set for the plugin",
"anyOf": [
{
"$ref": "#/definitions/DefaultPermission"
},
{
"type": "null"
}
]
},
"set": {
"description": "A list of permissions sets defined",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
}
},
"definitions": {
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": ["permissions"],
"properties": {
"version": {
"description": "The version of the permission.",
"type": ["integer", "null"],
"format": "uint64",
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": ["string", "null"]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": ["description", "identifier", "permissions"],
"properties": {
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": ["identifier"],
"properties": {
"version": {
"description": "The version of the permission.",
"type": ["integer", "null"],
"format": "uint64",
"minimum": 1.0
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": ["string", "null"]
},
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"platforms": {
"description": "Target platforms this permission applies. By default all platforms are affected by this permission.",
"type": ["array", "null"],
"items": {
"$ref": "#/definitions/Target"
}
}
}
},
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
"properties": {
"allow": {
"description": "Allowed command.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"deny": {
"description": "Denied command, which takes priority.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
}
},
"Scopes": {
"description": "An argument for fine grained behavior control of Tauri commands.\n\nIt can be of any serde serializable type and is used to allow or prevent certain actions inside a Tauri command. The configured scope is passed to the command and will be enforced by the command implementation.\n\n## Example\n\n```json { \"allow\": [{ \"path\": \"$HOME/**\" }], \"deny\": [{ \"path\": \"$HOME/secret.txt\" }] } ```",
"type": "object",
"properties": {
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": ["array", "null"],
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.",
"type": ["array", "null"],
"items": {
"$ref": "#/definitions/Value"
}
}
}
},
"Value": {
"description": "All supported ACL values.",
"anyOf": [
{
"description": "Represents a null JSON value.",
"type": "null"
},
{
"description": "Represents a [`bool`].",
"type": "boolean"
},
{
"description": "Represents a valid ACL [`Number`].",
"allOf": [
{
"$ref": "#/definitions/Number"
}
]
},
{
"description": "Represents a [`String`].",
"type": "string"
},
{
"description": "Represents a list of other [`Value`]s.",
"type": "array",
"items": {
"$ref": "#/definitions/Value"
}
},
{
"description": "Represents a map of [`String`] keys to [`Value`]s.",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Value"
}
}
]
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"Target": {
"description": "Platform target.",
"oneOf": [
{
"description": "MacOS.",
"type": "string",
"enum": ["macOS"]
},
{
"description": "Windows.",
"type": "string",
"enum": ["windows"]
},
{
"description": "Linux.",
"type": "string",
"enum": ["linux"]
},
{
"description": "Android.",
"type": "string",
"enum": ["android"]
},
{
"description": "iOS.",
"type": "string",
"enum": ["iOS"]
}
]
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "Enables the hide_window command without any pre-configured scope.",
"type": "string",
"const": "allow-hide-window"
},
{
"description": "Denies the hide_window command without any pre-configured scope.",
"type": "string",
"const": "deny-hide-window"
},
{
"description": "Enables the show_window command without any pre-configured scope.",
"type": "string",
"const": "allow-show-window"
},
{
"description": "Denies the show_window command without any pre-configured scope.",
"type": "string",
"const": "deny-show-window"
},
{
"description": "Default permissions for the plugin",
"type": "string",
"const": "default"
}
]
}
}
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PermissionFile",
"description": "Permission file that can define a default permission, a set of permissions or a list of inlined permissions.",
"type": "object",
"properties": {
"default": {
"description": "The default permission set for the plugin",
"anyOf": [
{
"$ref": "#/definitions/DefaultPermission"
},
{
"type": "null"
}
]
},
"set": {
"description": "A list of permissions sets defined",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionSet"
}
},
"permission": {
"description": "A list of inlined permissions",
"default": [],
"type": "array",
"items": {
"$ref": "#/definitions/Permission"
}
}
},
"definitions": {
"DefaultPermission": {
"description": "The default permission set of the plugin.\n\nWorks similarly to a permission with the \"default\" identifier.",
"type": "object",
"required": [
"permissions"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [
"string",
"null"
]
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"type": "string"
}
}
}
},
"PermissionSet": {
"description": "A set of direct permissions grouped together under a new name.",
"type": "object",
"required": [
"description",
"identifier",
"permissions"
],
"properties": {
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does.",
"type": "string"
},
"permissions": {
"description": "All permissions this set contains.",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionKind"
}
}
}
},
"Permission": {
"description": "Descriptions of explicit privileges of commands.\n\nIt can enable commands to be accessible in the frontend of the application.\n\nIf the scope is defined it can be used to fine grain control the access of individual or multiple commands.",
"type": "object",
"required": [
"identifier"
],
"properties": {
"version": {
"description": "The version of the permission.",
"type": [
"integer",
"null"
],
"format": "uint64",
"minimum": 1.0
},
"identifier": {
"description": "A unique identifier for the permission.",
"type": "string"
},
"description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [
"string",
"null"
]
},
"commands": {
"description": "Allowed or denied commands when using this permission.",
"default": {
"allow": [],
"deny": []
},
"allOf": [
{
"$ref": "#/definitions/Commands"
}
]
},
"scope": {
"description": "Allowed or denied scoped when using this permission.",
"allOf": [
{
"$ref": "#/definitions/Scopes"
}
]
},
"platforms": {
"description": "Target platforms this permission applies. By default all platforms are affected by this permission.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Target"
}
}
}
},
"Commands": {
"description": "Allowed and denied commands inside a permission.\n\nIf two commands clash inside of `allow` and `deny`, it should be denied by default.",
"type": "object",
"properties": {
"allow": {
"description": "Allowed command.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
},
"deny": {
"description": "Denied command, which takes priority.",
"default": [],
"type": "array",
"items": {
"type": "string"
}
}
}
},
"Scopes": {
"description": "An argument for fine grained behavior control of Tauri commands.\n\nIt can be of any serde serializable type and is used to allow or prevent certain actions inside a Tauri command. The configured scope is passed to the command and will be enforced by the command implementation.\n\n## Example\n\n```json { \"allow\": [{ \"path\": \"$HOME/**\" }], \"deny\": [{ \"path\": \"$HOME/secret.txt\" }] } ```",
"type": "object",
"properties": {
"allow": {
"description": "Data that defines what is allowed by the scope.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
},
"deny": {
"description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/Value"
}
}
}
},
"Value": {
"description": "All supported ACL values.",
"anyOf": [
{
"description": "Represents a null JSON value.",
"type": "null"
},
{
"description": "Represents a [`bool`].",
"type": "boolean"
},
{
"description": "Represents a valid ACL [`Number`].",
"allOf": [
{
"$ref": "#/definitions/Number"
}
]
},
{
"description": "Represents a [`String`].",
"type": "string"
},
{
"description": "Represents a list of other [`Value`]s.",
"type": "array",
"items": {
"$ref": "#/definitions/Value"
}
},
{
"description": "Represents a map of [`String`] keys to [`Value`]s.",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/Value"
}
}
]
},
"Number": {
"description": "A valid ACL number.",
"anyOf": [
{
"description": "Represents an [`i64`].",
"type": "integer",
"format": "int64"
},
{
"description": "Represents a [`f64`].",
"type": "number",
"format": "double"
}
]
},
"Target": {
"description": "Platform target.",
"oneOf": [
{
"description": "MacOS.",
"type": "string",
"enum": [
"macOS"
]
},
{
"description": "Windows.",
"type": "string",
"enum": [
"windows"
]
},
{
"description": "Linux.",
"type": "string",
"enum": [
"linux"
]
},
{
"description": "Android.",
"type": "string",
"enum": [
"android"
]
},
{
"description": "iOS.",
"type": "string",
"enum": [
"iOS"
]
}
]
},
"PermissionKind": {
"type": "string",
"oneOf": [
{
"description": "Enables the hide_window command without any pre-configured scope.",
"type": "string",
"const": "allow-hide-window"
},
{
"description": "Denies the hide_window command without any pre-configured scope.",
"type": "string",
"const": "deny-hide-window"
},
{
"description": "Enables the show_window command without any pre-configured scope.",
"type": "string",
"const": "allow-show-window"
},
{
"description": "Denies the show_window command without any pre-configured scope.",
"type": "string",
"const": "deny-show-window"
},
{
"description": "Default permissions for the plugin",
"type": "string",
"const": "default"
}
]
}
}
}

View File

@@ -1,51 +1,59 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "BongoCat",
"version": "../package.json",
"identifier": "com.ayangweb.BongoCat",
"build": {
"beforeDevCommand": "pnpm dev",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "pnpm build",
"frontendDist": "../dist"
},
"app": {
"macOSPrivateApi": true,
"windows": [
{
"label": "main",
"title": "BongoCat",
"url": "index.html/#/",
"width": 408,
"height": 236,
"alwaysOnTop": true,
"transparent": true,
"decorations": false,
"acceptFirstMouse": true
},
{
"label": "preference",
"title": "偏好设置",
"url": "index.html/#/preference",
"visible": false
}
],
"security": {
"csp": null
}
},
"bundle": {
"active": true,
"createUpdaterArtifacts": true,
"targets": ["nsis", "dmg", "app", "appimage", "deb", "rpm"],
"shortDescription": "BongoCat",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": ["assets/tray.png"]
}
"$schema": "https://schema.tauri.app/config/2",
"productName": "BongoCat",
"version": "../package.json",
"identifier": "com.ayangweb.BongoCat",
"build": {
"beforeDevCommand": "pnpm dev",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "pnpm build",
"frontendDist": "../dist"
},
"app": {
"macOSPrivateApi": true,
"windows": [
{
"label": "main",
"title": "BongoCat",
"url": "index.html/#/",
"width": 408,
"height": 236,
"alwaysOnTop": true,
"transparent": true,
"decorations": false,
"acceptFirstMouse": true
},
{
"label": "preference",
"title": "偏好设置",
"url": "index.html/#/preference",
"visible": false
}
],
"security": {
"csp": null
}
},
"bundle": {
"active": true,
"createUpdaterArtifacts": true,
"targets": ["nsis", "dmg", "app", "appimage", "deb", "rpm"],
"shortDescription": "BongoCat",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": ["assets/tray.png"]
},
"plugins": {
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEVBRjJFMzE3MjEwMUZEMTAKUldRUS9RRWhGK1B5NmdkemhKcUFrVjZBQXlzdExpakdWVEJDeU9XckVsbzV2cFIycVJOempWa2UK",
"endpoints": [
"https://github.com/ayangweb/BongoCat/releases/latest/download/latest.json"
]
}
}
}

View File

@@ -1,15 +1,15 @@
{
"identifier": "com.ayangweb.BongoCat",
"bundle": {
"linux": {
"deb": {
"depends": ["gstreamer1.0-plugins-good"],
"desktopTemplate": "./BongoCat.desktop"
},
"rpm": {
"depends": ["gstreamer1-plugins-good"],
"desktopTemplate": "./BongoCat.desktop"
}
}
}
"identifier": "com.ayangweb.BongoCat",
"bundle": {
"linux": {
"deb": {
"depends": ["gstreamer1.0-plugins-good"],
"desktopTemplate": "./BongoCat.desktop"
},
"rpm": {
"depends": ["gstreamer1-plugins-good"],
"desktopTemplate": "./BongoCat.desktop"
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"identifier": "com.ayangweb.BongoCat",
"bundle": {
"resources": ["assets/tray-mac.png"]
}
"identifier": "com.ayangweb.BongoCat",
"bundle": {
"resources": ["assets/tray-mac.png"]
}
}

View File

@@ -1,12 +1,12 @@
{
"identifier": "com.ayangweb.BongoCat",
"bundle": {
"windows": {
"digestAlgorithm": "sha256",
"nsis": {
"languages": ["SimpChinese"],
"installMode": "both"
}
}
}
"identifier": "com.ayangweb.BongoCat",
"bundle": {
"windows": {
"digestAlgorithm": "sha256",
"nsis": {
"languages": ["SimpChinese"],
"installMode": "both"
}
}
}
}

View File

@@ -1,29 +0,0 @@
import { listen } from "@tauri-apps/api/event";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { useEffect } from "react";
import { RouterProvider } from "react-router-dom";
import { LISTEN_KEY } from "./constants";
import { hideWindow, showWindow } from "./plugins/window";
import { router } from "./router";
const App = () => {
useEffect(() => {
const appWindow = getCurrentWebviewWindow();
listen(LISTEN_KEY.SHOW_WINDOW, ({ payload }) => {
if (appWindow.label !== payload) return;
showWindow();
});
listen(LISTEN_KEY.HIDE_WINDOW, ({ payload }) => {
if (appWindow.label !== payload) return;
hideWindow();
});
}, []);
return <RouterProvider router={router} />;
};
export default App;

28
src/App.vue Normal file
View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import { listen } from '@tauri-apps/api/event'
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
import { onMounted } from 'vue'
import { RouterView } from 'vue-router'
import { LISTEN_KEY } from './constants'
import { hideWindow, showWindow } from './plugins/window'
onMounted(() => {
const appWindow = getCurrentWebviewWindow()
listen(LISTEN_KEY.SHOW_WINDOW, ({ payload }) => {
if (appWindow.label !== payload) return
showWindow()
})
listen(LISTEN_KEY.HIDE_WINDOW, ({ payload }) => {
if (appWindow.label !== payload) return
hideWindow()
})
})
</script>
<template>
<RouterView />
</template>

View File

@@ -1,6 +1,6 @@
@font-face {
font-family: "cat";
src: url("../fonts/cat.woff2") format("woff2");
font-family: 'cat';
src: url('../fonts/cat.woff2') format('woff2');
}
html {

View File

@@ -0,0 +1,70 @@
import { listen } from '@tauri-apps/api/event'
import { onMounted, ref } from 'vue'
type MouseButtonValue = 'Left' | 'Right' | 'Middle'
interface MouseButtonEvent {
kind: 'MousePress' | 'MouseRelease'
value: MouseButtonValue
}
interface MouseMoveValue {
x: number
y: number
}
interface MouseMoveEvent {
kind: 'MouseMove'
value: MouseMoveValue
}
interface KeyboardEvent {
kind: 'KeyboardPress' | 'KeyboardRelease'
value: string
}
type DeviceEvent = MouseButtonEvent | MouseMoveEvent | KeyboardEvent
export function useDevice() {
const pressedMouses = ref<MouseButtonValue[]>([])
const mousePosition = ref<MouseMoveValue | undefined>()
const pressedKeys = ref<string[]>([])
const handlePress = <T>(array: T[], value: T) => {
return [...new Set([...array, value])]
}
const handleRelease = <T>(array: T[], value: T) => {
return array.filter(item => item !== value)
}
onMounted(() => {
listen<DeviceEvent>('change', ({ payload }) => {
const { kind, value } = payload
switch (kind) {
case 'MousePress':
pressedMouses.value = handlePress(pressedMouses.value, value)
break
case 'MouseRelease':
pressedMouses.value = handleRelease(pressedMouses.value, value)
break
case 'MouseMove':
mousePosition.value = value
break
case 'KeyboardPress':
pressedKeys.value = handlePress(pressedKeys.value, value)
break
case 'KeyboardRelease':
pressedKeys.value = handleRelease(pressedKeys.value, value)
break
}
})
})
return {
pressedMouses,
pressedKeys,
mousePosition,
}
}

View File

@@ -0,0 +1,67 @@
import type { IApplicationOptions } from 'pixi.js'
import { Live2DModel } from 'pixi-live2d-display'
import { Application } from 'pixi.js'
import { ref } from 'vue'
interface Options extends IApplicationOptions {
url: string
}
interface Motion {
Name: string
File: string
Sound?: string
FadeInTime: number
FadeOutTime: number
}
interface Expression {
Name: string
File: string
}
export function useModel() {
const model = ref<Live2DModel>()
const motions = ref<Record<string, Motion[]>>({})
const expressions = ref<Expression[]>([])
const loadModel = async (options: Options) => {
const { url, ...rest } = options
destroyModel()
const app = new Application({
resizeTo: window,
backgroundAlpha: 0,
...rest,
})
const loadedModel = await Live2DModel.from(url)
app.stage.addChild(loadedModel)
const { definitions, expressionManager }
= loadedModel.internalModel.motionManager
model.value = loadedModel
motions.value = definitions as Record<string, Motion[]>
expressions.value = expressionManager?.definitions as Expression[]
}
const destroyModel = () => {
model.value?.destroy()
}
const setParameterValue = (id: string, value: number | boolean) => {
return model.value?.internalModel.coreModel.setParameterValueById(id, Number(value))
}
return {
model,
motions,
expressions,
loadModel,
destroyModel,
setParameterValue,
}
}

111
src/composables/useTray.ts Normal file
View File

@@ -0,0 +1,111 @@
import type { TrayIconOptions } from '@tauri-apps/api/tray'
import { getName, getVersion } from '@tauri-apps/api/app'
import { Menu, MenuItem, PredefinedMenuItem } from '@tauri-apps/api/menu'
import { resolveResource } from '@tauri-apps/api/path'
import { TrayIcon } from '@tauri-apps/api/tray'
import { openUrl } from '@tauri-apps/plugin-opener'
import { exit, relaunch } from '@tauri-apps/plugin-process'
import { onMounted, ref, watch } from 'vue'
import { hideWindow, showWindow } from '../plugins/window'
import { isMac } from '../utils/platform'
const TRAY_ID = 'BONGO_CAT_TRAY'
export function useTray() {
const visible = ref(true)
onMounted(() => {
createTray()
})
watch(visible, () => {
updateTrayMenu()
})
const createTray = async () => {
const tray = await getTrayById()
if (tray) return
const appName = await getName()
const appVersion = await getVersion()
const menu = await getTrayMenu()
const iconPath = isMac ? 'assets/tray-mac.png' : 'assets/tray.png'
const icon = await resolveResource(iconPath)
const options: TrayIconOptions = {
menu,
icon,
id: TRAY_ID,
tooltip: `${appName} v${appVersion}`,
iconAsTemplate: true,
menuOnLeftClick: true,
}
return TrayIcon.new(options)
}
const getTrayById = () => {
return TrayIcon.getById(TRAY_ID)
}
const getTrayMenu = async () => {
const appVersion = await getVersion()
const items = await Promise.all([
MenuItem.new({
text: '偏好设置...',
accelerator: 'Cmd+,',
action: () => showWindow(),
}),
MenuItem.new({
text: visible.value ? '隐藏猫咪' : '显示猫咪',
action: () => {
if (visible.value) {
hideWindow('main')
} else {
showWindow('main')
}
visible.value = !visible.value
},
}),
PredefinedMenuItem.new({ item: 'Separator' }),
MenuItem.new({
text: '检查更新',
}),
MenuItem.new({
text: '开源地址',
action: () => openUrl('https://github.com/ayangweb/BongoCat'),
}),
PredefinedMenuItem.new({ item: 'Separator' }),
MenuItem.new({
text: `版本 ${appVersion}`,
enabled: false,
}),
MenuItem.new({
text: '重启应用',
action: relaunch,
}),
MenuItem.new({
text: '退出应用',
accelerator: 'Cmd+Q',
action: () => exit(0),
}),
])
return Menu.new({ items })
}
const updateTrayMenu = async () => {
const tray = await getTrayById()
if (!tray) return
const menu = await getTrayMenu()
tray.setMenu(menu)
}
}

View File

@@ -1,4 +1,4 @@
export const LISTEN_KEY = {
SHOW_WINDOW: "show-window",
HIDE_WINDOW: "hide-window",
};
SHOW_WINDOW: 'show-window',
HIDE_WINDOW: 'hide-window',
}

View File

@@ -1,67 +0,0 @@
import { listen } from "@tauri-apps/api/event";
import { type Dispatch, type SetStateAction, useEffect, useState } from "react";
type MouseButtonValue = "Left" | "Right" | "Middle";
type MouseButtonEvent = {
kind: "MousePress" | "MouseRelease";
value: MouseButtonValue;
};
type MouseMoveValue = {
x: number;
y: number;
};
type MouseMoveEvent = {
kind: "MouseMove";
value: MouseMoveValue;
};
type KeyboardEvent = {
kind: "KeyboardPress" | "KeyboardRelease";
value: string;
};
type DeviceEvent = MouseButtonEvent | MouseMoveEvent | KeyboardEvent;
type Setter<T> = Dispatch<SetStateAction<T[]>>;
export const useDevice = () => {
const [pressedMouses, setPressedMouses] = useState<MouseButtonValue[]>([]);
const [mousePosition, setMousePosition] = useState<MouseMoveValue>();
const [pressedKeys, setPressedKeys] = useState<string[]>([]);
useEffect(() => {
listen<DeviceEvent>("change", ({ payload }) => {
const { kind, value } = payload;
switch (kind) {
case "MousePress":
return handlePress(setPressedMouses, value);
case "MouseRelease":
return handleRelease(setPressedMouses, value);
case "MouseMove":
return setMousePosition(value);
case "KeyboardPress":
return handlePress(setPressedKeys, value);
case "KeyboardRelease":
return handleRelease(setPressedKeys, value);
}
});
}, []);
const handlePress = <T>(setter: Setter<T>, value: T) => {
setter((prev) => [...new Set([...prev, value])]);
};
const handleRelease = <T>(setter: Setter<T>, value: T) => {
setter((prev) => prev.filter((item) => item !== value));
};
return {
pressedMouses,
pressedKeys,
mousePosition,
};
};

View File

@@ -1,68 +0,0 @@
import { Live2DModel } from "pixi-live2d-display";
import { Application } from "pixi.js";
import { type RefObject, useCallback, useState } from "react";
interface Motion {
Name: string;
File: string;
Sound?: string;
FadeInTime: number;
FadeOutTime: number;
}
interface Expression {
Name: string;
File: string;
}
export const useModel = (container: RefObject<HTMLCanvasElement>) => {
const [model, setModel] = useState<Live2DModel>();
const [motions, setMotions] = useState<Record<string, Motion[]>>({});
const [expressions, setExpressions] = useState<Expression[]>([]);
const loadModel = async (url: string) => {
if (!container.current) return;
destroyModel();
const app = new Application({
view: container.current,
resizeTo: window,
backgroundAlpha: 0,
});
const model = await Live2DModel.from(url);
app.stage.addChild(model);
const { definitions, expressionManager } =
model.internalModel.motionManager;
setModel(model);
setMotions(definitions as Record<string, Motion[]>);
setExpressions(expressionManager?.definitions as Expression[]);
};
const destroyModel = () => {
model?.destroy();
};
const setParameterValue = useCallback(
(id: string, value: number | boolean) => {
return model?.internalModel.coreModel.setParameterValueById(
id,
Number(value),
);
},
[model],
);
return {
model,
motions,
expressions,
loadModel,
destroyModel,
setParameterValue,
};
};

View File

@@ -1,109 +0,0 @@
import { getName, getVersion } from "@tauri-apps/api/app";
import { Menu, MenuItem, PredefinedMenuItem } from "@tauri-apps/api/menu";
import { resolveResource } from "@tauri-apps/api/path";
import { TrayIcon, type TrayIconOptions } from "@tauri-apps/api/tray";
import { exit, relaunch } from "@tauri-apps/plugin-process";
import { useEffect, useState } from "react";
import { hideWindow, showWindow } from "../plugins/window";
import { isMac } from "../utils/platform";
const TRAY_ID = "BONGO_CAT_TRAY";
export const useTray = () => {
const [visible, setVisible] = useState(true);
useEffect(() => {
updateTrayMenu();
}, [visible]);
const getTrayById = () => {
return TrayIcon.getById(TRAY_ID);
};
const createTray = async () => {
const tray = await getTrayById();
if (tray) return;
const appName = await getName();
const appVersion = await getVersion();
const menu = await getTrayMenu();
const iconPath = isMac ? "assets/tray-mac.png" : "assets/tray.png";
const icon = await resolveResource(iconPath);
const options: TrayIconOptions = {
menu,
icon,
id: TRAY_ID,
tooltip: `${appName} v${appVersion}`,
iconAsTemplate: true,
menuOnLeftClick: true,
};
return TrayIcon.new(options);
};
const getTrayMenu = async () => {
const appVersion = await getVersion();
const items = await Promise.all([
MenuItem.new({
text: "偏好设置...",
accelerator: "Cmd+,",
action: () => showWindow(),
}),
MenuItem.new({
text: visible ? "隐藏猫咪" : "显示猫咪",
action: () => {
if (visible) {
hideWindow("main");
setVisible(false);
} else {
showWindow("main");
setVisible(true);
}
},
}),
PredefinedMenuItem.new({ item: "Separator" }),
MenuItem.new({
text: "检查更新",
}),
MenuItem.new({
text: "开源地址",
// action: () => open(GITHUB_LINK),
}),
PredefinedMenuItem.new({ item: "Separator" }),
MenuItem.new({
text: `版本 ${appVersion}`,
enabled: false,
}),
MenuItem.new({
text: "重启应用",
action: relaunch,
}),
MenuItem.new({
text: "退出应用",
accelerator: "Cmd+Q",
action: () => exit(0),
}),
]);
return Menu.new({ items });
};
const updateTrayMenu = async () => {
const tray = await getTrayById();
if (!tray) {
return createTray();
}
const menu = await getTrayMenu();
tray.setMenu(menu);
};
};

8
src/main.ts Normal file
View File

@@ -0,0 +1,8 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import 'virtual:uno.css'
import '@unocss/reset/tailwind-compat.css'
import './assets/css/global.css'
createApp(App).use(router).mount('#app')

View File

@@ -1,9 +0,0 @@
import ReactDOM from "react-dom/client";
import App from "./App";
import "virtual:uno.css";
import "@unocss/reset/tailwind-compat.css";
import "./assets/css/global.css";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<App />,
);

View File

@@ -1,101 +0,0 @@
import { LogicalSize } from "@tauri-apps/api/dpi";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { Live2DModel } from "pixi-live2d-display";
import { Ticker } from "pixi.js";
import { Fragment, useEffect, useRef, useState } from "react";
import { useDevice } from "../../hooks/useDevice";
import { useModel } from "../../hooks/useModel";
Live2DModel.registerTicker(Ticker);
const Main = () => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const [mode] = useState("standard");
const { pressedKeys, pressedMouses, mousePosition } = useDevice();
const { model, loadModel, setParameterValue } = useModel(canvasRef);
useEffect(() => {
loadModel(`/models/${mode}/cat.model3.json`);
}, [mode]);
useEffect(() => {
if (!model) return;
handleResized();
window.removeEventListener("resize", handleResized);
window.addEventListener("resize", handleResized);
}, [model]);
useEffect(() => {
const hasArrowKey = pressedKeys.some((key) => key.endsWith("Arrow"));
const hasNonArrowKey = pressedKeys.some((key) => !key.endsWith("Arrow"));
setParameterValue("CatParamRightHandDown", hasArrowKey);
setParameterValue("CatParamLeftHandDown", hasNonArrowKey);
}, [pressedKeys]);
useEffect(() => {
const isLeftDown = pressedMouses.includes("Left");
const isRightDown = pressedMouses.includes("Right");
setParameterValue("ParamMouseLeftDown", isLeftDown);
setParameterValue("ParamMouseRightDown", isRightDown);
}, [pressedMouses]);
useEffect(() => {
if (!mousePosition) return;
const { innerWidth, innerHeight } = window;
const x = -30 + (mousePosition.x / innerWidth) * 60;
const y = -30 + (mousePosition.y / innerHeight) * 60;
setParameterValue("ParamMouseX", -x);
setParameterValue("ParamMouseY", -y);
setParameterValue("ParamAngleX", x);
setParameterValue("ParamAngleY", -y);
}, [mousePosition]);
const handleResized = async () => {
if (!model) return;
const { innerWidth } = window;
await getCurrentWebviewWindow().setSize(
new LogicalSize({
width: innerWidth,
height: innerWidth * (354 / 612),
}),
);
model.scale.set(innerWidth / 612);
};
const handleMouseDown = () => {
const appWindow = getCurrentWebviewWindow();
appWindow.startDragging();
};
return (
<div
className="children:absolute relative children:h-screen children:w-screen"
onMouseDown={handleMouseDown}
>
<img src={`/images/backgrounds/${mode}.png`} />
<canvas ref={canvasRef} />
{pressedKeys.map((item) => (
<Fragment key={item}>
<img src={`/images/keys/${item}.png`} />
<img src={`/images/hands/${item}.png`} />
</Fragment>
))}
</div>
);
};
export default Main;

View File

@@ -1,9 +0,0 @@
import { useTray } from "../../hooks/useTray";
const Preference = () => {
useTray();
return <div>Preference</div>;
};
export default Preference;

103
src/pages/main.vue Normal file
View File

@@ -0,0 +1,103 @@
<script setup lang="ts">
import { LogicalSize } from '@tauri-apps/api/dpi'
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
import { Live2DModel } from 'pixi-live2d-display'
import { Ticker } from 'pixi.js'
import { ref, useTemplateRef, watch, watchEffect } from 'vue'
import { useDevice } from '../composables/useDevice'
import { useModel } from '../composables/useModel'
Live2DModel.registerTicker(Ticker)
const canvasRef = useTemplateRef<HTMLCanvasElement>('canvasRef')
const mode = ref('standard')
const { pressedKeys, pressedMouses, mousePosition } = useDevice()
const { model, loadModel, setParameterValue } = useModel()
watch([mode, canvasRef], (value) => {
const [mode, canvas] = value
if (!canvas) return
loadModel({
view: canvas,
url: `/models/${mode}/cat.model3.json`,
})
}, { immediate: true })
watchEffect(() => {
if (!model.value) return
handleResized()
window.removeEventListener('resize', handleResized)
window.addEventListener('resize', handleResized)
})
watchEffect(() => {
const hasArrowKey = pressedKeys.value.some(key => key.endsWith('Arrow'))
const hasNonArrowKey = pressedKeys.value.some(key => !key.endsWith('Arrow'))
setParameterValue('CatParamRightHandDown', hasArrowKey)
setParameterValue('CatParamLeftHandDown', hasNonArrowKey)
})
watchEffect(() => {
const isLeftDown = pressedMouses.value.includes('Left')
const isRightDown = pressedMouses.value.includes('Right')
setParameterValue('ParamMouseLeftDown', isLeftDown)
setParameterValue('ParamMouseRightDown', isRightDown)
})
watchEffect(() => {
if (!mousePosition.value) return
const { innerWidth, innerHeight } = window
const x = -30 + (mousePosition.value.x / innerWidth) * 15
const y = -30 + (mousePosition.value.y / innerHeight) * 15
setParameterValue('ParamMouseX', -x)
setParameterValue('ParamMouseY', -y)
setParameterValue('ParamAngleX', x)
setParameterValue('ParamAngleY', -y)
})
async function handleResized() {
if (!model.value) return
const { innerWidth } = window
await getCurrentWebviewWindow().setSize(
new LogicalSize({
width: innerWidth,
height: innerWidth * (354 / 612),
}),
)
model.value.scale.set(innerWidth / 612)
}
function handleMouseDown() {
const appWindow = getCurrentWebviewWindow()
appWindow.startDragging()
}
</script>
<template>
<div
class="relative children:(absolute h-screen w-screen)"
@mousedown="handleMouseDown"
>
<img :src="`/images/backgrounds/${mode}.png`">
<canvas ref="canvasRef" />
<template v-for="item in pressedKeys" :key="item">
<img :src="`/images/keys/${item}.png`">
<img :src="`/images/hands/${item}.png`">
</template>
</div>
</template>

11
src/pages/preference.vue Normal file
View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
import { useTray } from '../composables/useTray'
useTray()
</script>
<template>
<div class="">
Preference
</div>
</template>../../composables/useTray

View File

@@ -1,26 +1,26 @@
import { invoke } from "@tauri-apps/api/core";
import { emit } from "@tauri-apps/api/event";
import { LISTEN_KEY } from "../constants";
import { invoke } from '@tauri-apps/api/core'
import { emit } from '@tauri-apps/api/event'
import { LISTEN_KEY } from '../constants'
type WindowLabel = "main" | "preference";
type WindowLabel = 'main' | 'preference'
const COMMAND = {
SHOW_WINDOW: "plugin:custom-window|show_window",
HIDE_WINDOW: "plugin:custom-window|hide_window",
};
SHOW_WINDOW: 'plugin:custom-window|show_window',
HIDE_WINDOW: 'plugin:custom-window|hide_window',
}
export const showWindow = (label?: WindowLabel) => {
if (label) {
emit(LISTEN_KEY.SHOW_WINDOW, label);
} else {
invoke(COMMAND.SHOW_WINDOW);
}
};
export function showWindow(label?: WindowLabel) {
if (label) {
emit(LISTEN_KEY.SHOW_WINDOW, label)
} else {
invoke(COMMAND.SHOW_WINDOW)
}
}
export const hideWindow = (label?: WindowLabel) => {
if (label) {
emit(LISTEN_KEY.HIDE_WINDOW, label);
} else {
invoke(COMMAND.HIDE_WINDOW);
}
};
export function hideWindow(label?: WindowLabel) {
if (label) {
emit(LISTEN_KEY.HIDE_WINDOW, label)
} else {
invoke(COMMAND.HIDE_WINDOW)
}
}

View File

@@ -1,14 +1,21 @@
import { createHashRouter } from "react-router-dom";
import Main from "../pages/Main";
import Preference from "../pages/Preference";
import { createRouter, createWebHashHistory } from 'vue-router'
import Main from '../pages/main.vue'
import Preference from '../pages/preference.vue'
export const router = createHashRouter([
{
path: "/",
Component: Main,
},
{
path: "/preference",
Component: Preference,
},
]);
const routes = [
{
path: '/',
component: Main,
},
{
path: '/preference',
component: Preference,
},
]
const router = createRouter({
history: createWebHashHistory(),
routes,
})
export default router

View File

@@ -1,3 +1,3 @@
declare interface Window {
availableKeys: string[];
availableKeys: string[]
}

View File

@@ -1,7 +1,7 @@
import { platform } from "@tauri-apps/plugin-os";
import { platform } from '@tauri-apps/plugin-os'
export const isMac = platform() === "macos";
export const isMac = platform() === 'macos'
export const isWindows = platform() === "windows";
export const isWindows = platform() === 'windows'
export const isLinux = platform() === "linux";
export const isLinux = platform() === 'linux'

View File

@@ -1,25 +1,25 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"compilerOptions": {
"target": "ES2020",
"jsx": "react-jsx",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"useDefineForClassFields": true,
"module": "ESNext",
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Bundler mode */
"moduleResolution": "bundler",
"resolveJsonModule": true,
"allowImportingTsExtensions": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
/* Linting */
"strict": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noEmit": true,
"isolatedModules": true,
"skipLibCheck": true
},
"references": [{ "path": "./tsconfig.node.json" }],
"include": ["src"]
}

View File

@@ -1,10 +1,10 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"skipLibCheck": true
},
"include": ["vite.config.ts"]
}

View File

@@ -1,24 +1,24 @@
import presetRemToPx from "@unocss/preset-rem-to-px";
import presetRemToPx from '@unocss/preset-rem-to-px'
import {
defineConfig,
presetIcons,
presetWind3,
transformerDirectives,
transformerVariantGroup,
} from "unocss";
defineConfig,
presetIcons,
presetWind3,
transformerDirectives,
transformerVariantGroup,
} from 'unocss'
export default defineConfig({
presets: [
presetWind3(),
presetIcons(),
presetRemToPx({
baseFontSize: 4,
}),
],
transformers: [
transformerVariantGroup(),
transformerDirectives({
applyVariable: ["--uno"],
}),
],
});
presets: [
presetWind3(),
presetIcons(),
presetRemToPx({
baseFontSize: 4,
}),
],
transformers: [
transformerVariantGroup(),
transformerDirectives({
applyVariable: ['--uno'],
}),
],
})

View File

@@ -1,45 +1,46 @@
import { readdirSync } from "node:fs";
import { parse, resolve } from "node:path";
import react from "@vitejs/plugin-react";
import UnoCSS from "unocss/vite";
import { defineConfig } from "vite";
import { readdirSync } from 'node:fs'
import { parse, resolve } from 'node:path'
import { env } from 'node:process'
import vue from '@vitejs/plugin-vue'
import UnoCSS from 'unocss/vite'
import { defineConfig } from 'vite'
const host = process.env.TAURI_DEV_HOST;
const host = env.TAURI_DEV_HOST
const availableKeys = readdirSync(resolve(__dirname, "public/images/keys"))
.filter((file) => !file.startsWith("."))
.map((file) => parse(file).name);
const availableKeys = readdirSync(resolve(__dirname, 'public/images/keys'))
.filter(file => !file.startsWith('.'))
.map(file => parse(file).name)
// https://vitejs.dev/config/
export default defineConfig(async () => ({
plugins: [react(), UnoCSS()],
resolve: {
alias: {
"@": "/src",
},
},
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//
// 1. prevent vite from obscuring rust errors
clearScreen: false,
// 2. tauri expects a fixed port, fail if that port is not available
server: {
port: 1420,
strictPort: true,
host: host || false,
hmr: host
? {
protocol: "ws",
host,
port: 1421,
}
: undefined,
watch: {
// 3. tell vite to ignore watching `src-tauri`
ignored: ["**/src-tauri/**"],
},
},
define: {
"window.availableKeys": availableKeys,
},
}));
plugins: [vue(), UnoCSS()],
resolve: {
alias: {
'@': '/src',
},
},
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//
// 1. prevent vite from obscuring rust errors
clearScreen: false,
// 2. tauri expects a fixed port, fail if that port is not available
server: {
port: 1420,
strictPort: true,
host: host || false,
hmr: host
? {
protocol: 'ws',
host,
port: 1421,
}
: undefined,
watch: {
// 3. tell vite to ignore watching `src-tauri`
ignored: ['**/src-tauri/**'],
},
},
define: {
'window.availableKeys': availableKeys,
},
}))