diff --git a/dev.moku.app.yml b/dev.moku.app.yml index 8175fe4..6c19492 100644 --- a/dev.moku.app.yml +++ b/dev.moku.app.yml @@ -181,7 +181,7 @@ modules: path: . - type: file path: packaging/frontend-dist.tar.gz - sha256: 43b7274bdab884aacbc3dad6f0f7c043d8e3d82b7bf7398e1df9f516ed553152 + sha256: d3ebde4d39e3de61420b78a9506df1a5c77c14d705e42662a45a2179bc96030e - packaging/cargo-sources.json - type: inline dest: src-tauri/.cargo diff --git a/flake.nix b/flake.nix index 114381e..700b83b 100644 --- a/flake.nix +++ b/flake.nix @@ -71,7 +71,7 @@ inherit version; src = frontendSrc; fetcherVersion = 1; - hash = "sha256-ezlckHhfSIe/Hs30tbiN0a/EuvGxhO5L020aup23Ozg="; + hash = "sha256-oJVMFL1wdAd1x4AQECTh/yPMhiirbjUduBelZ3bWQtI="; }; buildPhase = "pnpm build"; diff --git a/package.json b/package.json index c044e04..051e714 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "clsx": "^2.1.1", "phosphor-svelte": "^3.1.0", "svelte-spa-router": "^4.0.1", + "tauri-plugin-discord-rpc-api": "github:Youwes09/tauri-plugin-discord-rpc", "tauri-plugin-drpc": "^1.0.3" }, "devDependencies": { diff --git a/packaging/cargo-sources.json b/packaging/cargo-sources.json index b1edefe..aa5eeb4 100644 --- a/packaging/cargo-sources.json +++ b/packaging/cargo-sources.json @@ -2030,92 +2030,92 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/icu_collections/icu_collections-2.1.1.crate", - "sha256": "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43", - "dest": "cargo/vendor/icu_collections-2.1.1" + "url": "https://static.crates.io/crates/icu_collections/icu_collections-2.2.0.crate", + "sha256": "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c", + "dest": "cargo/vendor/icu_collections-2.2.0" }, { "type": "inline", - "contents": "{\"package\": \"4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43\", \"files\": {}}", - "dest": "cargo/vendor/icu_collections-2.1.1", + "contents": "{\"package\": \"2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c\", \"files\": {}}", + "dest": "cargo/vendor/icu_collections-2.2.0", "dest-filename": ".cargo-checksum.json" }, { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/icu_locale_core/icu_locale_core-2.1.1.crate", - "sha256": "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6", - "dest": "cargo/vendor/icu_locale_core-2.1.1" + "url": "https://static.crates.io/crates/icu_locale_core/icu_locale_core-2.2.0.crate", + "sha256": "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29", + "dest": "cargo/vendor/icu_locale_core-2.2.0" }, { "type": "inline", - "contents": "{\"package\": \"edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6\", \"files\": {}}", - "dest": "cargo/vendor/icu_locale_core-2.1.1", + "contents": "{\"package\": \"92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29\", \"files\": {}}", + "dest": "cargo/vendor/icu_locale_core-2.2.0", "dest-filename": ".cargo-checksum.json" }, { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/icu_normalizer/icu_normalizer-2.1.1.crate", - "sha256": "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599", - "dest": "cargo/vendor/icu_normalizer-2.1.1" + "url": "https://static.crates.io/crates/icu_normalizer/icu_normalizer-2.2.0.crate", + "sha256": "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4", + "dest": "cargo/vendor/icu_normalizer-2.2.0" }, { "type": "inline", - "contents": "{\"package\": \"5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599\", \"files\": {}}", - "dest": "cargo/vendor/icu_normalizer-2.1.1", + "contents": "{\"package\": \"c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4\", \"files\": {}}", + "dest": "cargo/vendor/icu_normalizer-2.2.0", "dest-filename": ".cargo-checksum.json" }, { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/icu_normalizer_data/icu_normalizer_data-2.1.1.crate", - "sha256": "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a", - "dest": "cargo/vendor/icu_normalizer_data-2.1.1" + "url": "https://static.crates.io/crates/icu_normalizer_data/icu_normalizer_data-2.2.0.crate", + "sha256": "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38", + "dest": "cargo/vendor/icu_normalizer_data-2.2.0" }, { "type": "inline", - "contents": "{\"package\": \"7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a\", \"files\": {}}", - "dest": "cargo/vendor/icu_normalizer_data-2.1.1", + "contents": "{\"package\": \"da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38\", \"files\": {}}", + "dest": "cargo/vendor/icu_normalizer_data-2.2.0", "dest-filename": ".cargo-checksum.json" }, { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/icu_properties/icu_properties-2.1.2.crate", - "sha256": "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec", - "dest": "cargo/vendor/icu_properties-2.1.2" + "url": "https://static.crates.io/crates/icu_properties/icu_properties-2.2.0.crate", + "sha256": "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de", + "dest": "cargo/vendor/icu_properties-2.2.0" }, { "type": "inline", - "contents": "{\"package\": \"020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec\", \"files\": {}}", - "dest": "cargo/vendor/icu_properties-2.1.2", + "contents": "{\"package\": \"bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de\", \"files\": {}}", + "dest": "cargo/vendor/icu_properties-2.2.0", "dest-filename": ".cargo-checksum.json" }, { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/icu_properties_data/icu_properties_data-2.1.2.crate", - "sha256": "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af", - "dest": "cargo/vendor/icu_properties_data-2.1.2" + "url": "https://static.crates.io/crates/icu_properties_data/icu_properties_data-2.2.0.crate", + "sha256": "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14", + "dest": "cargo/vendor/icu_properties_data-2.2.0" }, { "type": "inline", - "contents": "{\"package\": \"616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af\", \"files\": {}}", - "dest": "cargo/vendor/icu_properties_data-2.1.2", + "contents": "{\"package\": \"8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14\", \"files\": {}}", + "dest": "cargo/vendor/icu_properties_data-2.2.0", "dest-filename": ".cargo-checksum.json" }, { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/icu_provider/icu_provider-2.1.1.crate", - "sha256": "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614", - "dest": "cargo/vendor/icu_provider-2.1.1" + "url": "https://static.crates.io/crates/icu_provider/icu_provider-2.2.0.crate", + "sha256": "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421", + "dest": "cargo/vendor/icu_provider-2.2.0" }, { "type": "inline", - "contents": "{\"package\": \"85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614\", \"files\": {}}", - "dest": "cargo/vendor/icu_provider-2.1.1", + "contents": "{\"package\": \"139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421\", \"files\": {}}", + "dest": "cargo/vendor/icu_provider-2.2.0", "dest-filename": ".cargo-checksum.json" }, { @@ -2186,14 +2186,14 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/indexmap/indexmap-2.13.0.crate", - "sha256": "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017", - "dest": "cargo/vendor/indexmap-2.13.0" + "url": "https://static.crates.io/crates/indexmap/indexmap-2.13.1.crate", + "sha256": "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff", + "dest": "cargo/vendor/indexmap-2.13.1" }, { "type": "inline", - "contents": "{\"package\": \"7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017\", \"files\": {}}", - "dest": "cargo/vendor/indexmap-2.13.0", + "contents": "{\"package\": \"45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff\", \"files\": {}}", + "dest": "cargo/vendor/indexmap-2.13.1", "dest-filename": ".cargo-checksum.json" }, { @@ -2459,14 +2459,14 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/libc/libc-0.2.183.crate", - "sha256": "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d", - "dest": "cargo/vendor/libc-0.2.183" + "url": "https://static.crates.io/crates/libc/libc-0.2.184.crate", + "sha256": "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af", + "dest": "cargo/vendor/libc-0.2.184" }, { "type": "inline", - "contents": "{\"package\": \"b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d\", \"files\": {}}", - "dest": "cargo/vendor/libc-0.2.183", + "contents": "{\"package\": \"48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af\", \"files\": {}}", + "dest": "cargo/vendor/libc-0.2.184", "dest-filename": ".cargo-checksum.json" }, { @@ -2511,14 +2511,14 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/litemap/litemap-0.8.1.crate", - "sha256": "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77", - "dest": "cargo/vendor/litemap-0.8.1" + "url": "https://static.crates.io/crates/litemap/litemap-0.8.2.crate", + "sha256": "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0", + "dest": "cargo/vendor/litemap-0.8.2" }, { "type": "inline", - "contents": "{\"package\": \"6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77\", \"files\": {}}", - "dest": "cargo/vendor/litemap-0.8.1", + "contents": "{\"package\": \"92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0\", \"files\": {}}", + "dest": "cargo/vendor/litemap-0.8.2", "dest-filename": ".cargo-checksum.json" }, { @@ -2719,14 +2719,14 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/muda/muda-0.17.1.crate", - "sha256": "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a", - "dest": "cargo/vendor/muda-0.17.1" + "url": "https://static.crates.io/crates/muda/muda-0.17.2.crate", + "sha256": "7c9fec5a4e89860383d778d10563a605838f8f0b2f9303868937e5ff32e86177", + "dest": "cargo/vendor/muda-0.17.2" }, { "type": "inline", - "contents": "{\"package\": \"01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a\", \"files\": {}}", - "dest": "cargo/vendor/muda-0.17.1", + "contents": "{\"package\": \"7c9fec5a4e89860383d778d10563a605838f8f0b2f9303868937e5ff32e86177\", \"files\": {}}", + "dest": "cargo/vendor/muda-0.17.2", "dest-filename": ".cargo-checksum.json" }, { @@ -3577,14 +3577,14 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/potential_utf/potential_utf-0.1.4.crate", - "sha256": "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77", - "dest": "cargo/vendor/potential_utf-0.1.4" + "url": "https://static.crates.io/crates/potential_utf/potential_utf-0.1.5.crate", + "sha256": "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564", + "dest": "cargo/vendor/potential_utf-0.1.5" }, { "type": "inline", - "contents": "{\"package\": \"b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77\", \"files\": {}}", - "dest": "cargo/vendor/potential_utf-0.1.4", + "contents": "{\"package\": \"0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564\", \"files\": {}}", + "dest": "cargo/vendor/potential_utf-0.1.5", "dest-filename": ".cargo-checksum.json" }, { @@ -5514,14 +5514,14 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/tinystr/tinystr-0.8.2.crate", - "sha256": "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869", - "dest": "cargo/vendor/tinystr-0.8.2" + "url": "https://static.crates.io/crates/tinystr/tinystr-0.8.3.crate", + "sha256": "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d", + "dest": "cargo/vendor/tinystr-0.8.3" }, { "type": "inline", - "contents": "{\"package\": \"42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869\", \"files\": {}}", - "dest": "cargo/vendor/tinystr-0.8.2", + "contents": "{\"package\": \"c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d\", \"files\": {}}", + "dest": "cargo/vendor/tinystr-0.8.3", "dest-filename": ".cargo-checksum.json" }, { @@ -5696,27 +5696,27 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/toml_edit/toml_edit-0.25.9+spec-1.1.0.crate", - "sha256": "da053d28fe57e2c9d21b48261e14e7b4c8b670b54d2c684847b91feaf4c7dac5", - "dest": "cargo/vendor/toml_edit-0.25.9+spec-1.1.0" + "url": "https://static.crates.io/crates/toml_edit/toml_edit-0.25.10+spec-1.1.0.crate", + "sha256": "a82418ca169e235e6c399a84e395ab6debeb3bc90edc959bf0f48647c6a32d1b", + "dest": "cargo/vendor/toml_edit-0.25.10+spec-1.1.0" }, { "type": "inline", - "contents": "{\"package\": \"da053d28fe57e2c9d21b48261e14e7b4c8b670b54d2c684847b91feaf4c7dac5\", \"files\": {}}", - "dest": "cargo/vendor/toml_edit-0.25.9+spec-1.1.0", + "contents": "{\"package\": \"a82418ca169e235e6c399a84e395ab6debeb3bc90edc959bf0f48647c6a32d1b\", \"files\": {}}", + "dest": "cargo/vendor/toml_edit-0.25.10+spec-1.1.0", "dest-filename": ".cargo-checksum.json" }, { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/toml_parser/toml_parser-1.1.1+spec-1.1.0.crate", - "sha256": "39ca317ebc49f06bd748bfba29533eac9485569dc9bf80b849024b025e814fb9", - "dest": "cargo/vendor/toml_parser-1.1.1+spec-1.1.0" + "url": "https://static.crates.io/crates/toml_parser/toml_parser-1.1.2+spec-1.1.0.crate", + "sha256": "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526", + "dest": "cargo/vendor/toml_parser-1.1.2+spec-1.1.0" }, { "type": "inline", - "contents": "{\"package\": \"39ca317ebc49f06bd748bfba29533eac9485569dc9bf80b849024b025e814fb9\", \"files\": {}}", - "dest": "cargo/vendor/toml_parser-1.1.1+spec-1.1.0", + "contents": "{\"package\": \"a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526\", \"files\": {}}", + "dest": "cargo/vendor/toml_parser-1.1.2+spec-1.1.0", "dest-filename": ".cargo-checksum.json" }, { @@ -7438,14 +7438,14 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/writeable/writeable-0.6.2.crate", - "sha256": "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9", - "dest": "cargo/vendor/writeable-0.6.2" + "url": "https://static.crates.io/crates/writeable/writeable-0.6.3.crate", + "sha256": "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4", + "dest": "cargo/vendor/writeable-0.6.3" }, { "type": "inline", - "contents": "{\"package\": \"9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9\", \"files\": {}}", - "dest": "cargo/vendor/writeable-0.6.2", + "contents": "{\"package\": \"1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4\", \"files\": {}}", + "dest": "cargo/vendor/writeable-0.6.3", "dest-filename": ".cargo-checksum.json" }, { @@ -7503,27 +7503,27 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/yoke/yoke-0.8.1.crate", - "sha256": "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954", - "dest": "cargo/vendor/yoke-0.8.1" + "url": "https://static.crates.io/crates/yoke/yoke-0.8.2.crate", + "sha256": "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca", + "dest": "cargo/vendor/yoke-0.8.2" }, { "type": "inline", - "contents": "{\"package\": \"72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954\", \"files\": {}}", - "dest": "cargo/vendor/yoke-0.8.1", + "contents": "{\"package\": \"abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca\", \"files\": {}}", + "dest": "cargo/vendor/yoke-0.8.2", "dest-filename": ".cargo-checksum.json" }, { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/yoke-derive/yoke-derive-0.8.1.crate", - "sha256": "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d", - "dest": "cargo/vendor/yoke-derive-0.8.1" + "url": "https://static.crates.io/crates/yoke-derive/yoke-derive-0.8.2.crate", + "sha256": "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e", + "dest": "cargo/vendor/yoke-derive-0.8.2" }, { "type": "inline", - "contents": "{\"package\": \"b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d\", \"files\": {}}", - "dest": "cargo/vendor/yoke-derive-0.8.1", + "contents": "{\"package\": \"de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e\", \"files\": {}}", + "dest": "cargo/vendor/yoke-derive-0.8.2", "dest-filename": ".cargo-checksum.json" }, { @@ -7555,27 +7555,27 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/zerofrom/zerofrom-0.1.6.crate", - "sha256": "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5", - "dest": "cargo/vendor/zerofrom-0.1.6" + "url": "https://static.crates.io/crates/zerofrom/zerofrom-0.1.7.crate", + "sha256": "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df", + "dest": "cargo/vendor/zerofrom-0.1.7" }, { "type": "inline", - "contents": "{\"package\": \"50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5\", \"files\": {}}", - "dest": "cargo/vendor/zerofrom-0.1.6", + "contents": "{\"package\": \"69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df\", \"files\": {}}", + "dest": "cargo/vendor/zerofrom-0.1.7", "dest-filename": ".cargo-checksum.json" }, { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/zerofrom-derive/zerofrom-derive-0.1.6.crate", - "sha256": "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502", - "dest": "cargo/vendor/zerofrom-derive-0.1.6" + "url": "https://static.crates.io/crates/zerofrom-derive/zerofrom-derive-0.1.7.crate", + "sha256": "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1", + "dest": "cargo/vendor/zerofrom-derive-0.1.7" }, { "type": "inline", - "contents": "{\"package\": \"d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502\", \"files\": {}}", - "dest": "cargo/vendor/zerofrom-derive-0.1.6", + "contents": "{\"package\": \"11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1\", \"files\": {}}", + "dest": "cargo/vendor/zerofrom-derive-0.1.7", "dest-filename": ".cargo-checksum.json" }, { @@ -7594,40 +7594,40 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/zerotrie/zerotrie-0.2.3.crate", - "sha256": "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851", - "dest": "cargo/vendor/zerotrie-0.2.3" + "url": "https://static.crates.io/crates/zerotrie/zerotrie-0.2.4.crate", + "sha256": "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf", + "dest": "cargo/vendor/zerotrie-0.2.4" }, { "type": "inline", - "contents": "{\"package\": \"2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851\", \"files\": {}}", - "dest": "cargo/vendor/zerotrie-0.2.3", + "contents": "{\"package\": \"0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf\", \"files\": {}}", + "dest": "cargo/vendor/zerotrie-0.2.4", "dest-filename": ".cargo-checksum.json" }, { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/zerovec/zerovec-0.11.5.crate", - "sha256": "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002", - "dest": "cargo/vendor/zerovec-0.11.5" + "url": "https://static.crates.io/crates/zerovec/zerovec-0.11.6.crate", + "sha256": "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239", + "dest": "cargo/vendor/zerovec-0.11.6" }, { "type": "inline", - "contents": "{\"package\": \"6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002\", \"files\": {}}", - "dest": "cargo/vendor/zerovec-0.11.5", + "contents": "{\"package\": \"90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239\", \"files\": {}}", + "dest": "cargo/vendor/zerovec-0.11.6", "dest-filename": ".cargo-checksum.json" }, { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/zerovec-derive/zerovec-derive-0.11.2.crate", - "sha256": "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3", - "dest": "cargo/vendor/zerovec-derive-0.11.2" + "url": "https://static.crates.io/crates/zerovec-derive/zerovec-derive-0.11.3.crate", + "sha256": "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555", + "dest": "cargo/vendor/zerovec-derive-0.11.3" }, { "type": "inline", - "contents": "{\"package\": \"eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3\", \"files\": {}}", - "dest": "cargo/vendor/zerovec-derive-0.11.2", + "contents": "{\"package\": \"625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555\", \"files\": {}}", + "dest": "cargo/vendor/zerovec-derive-0.11.3", "dest-filename": ".cargo-checksum.json" }, { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bee77ce..76fa749 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: svelte-spa-router: specifier: ^4.0.1 version: 4.0.2 + tauri-plugin-discord-rpc-api: + specifier: github:Youwes09/tauri-plugin-discord-rpc + version: https://codeload.github.com/Youwes09/tauri-plugin-discord-rpc/tar.gz/4b20388e4b65e0efcff2aa9a8622b5884554cd8a tauri-plugin-drpc: specifier: ^1.0.3 version: 1.0.3(typescript@5.9.3) @@ -747,6 +750,10 @@ packages: resolution: {integrity: sha512-TTDxwYnHkova6Wsyj1PGt9TByuWqvMoeY1bQiuAf2DM/JeDSMw7FjRKzk8K/5mJ99vGOKhbCqTDpyAKwjp4igg==} engines: {node: '>=18'} + tauri-plugin-discord-rpc-api@https://codeload.github.com/Youwes09/tauri-plugin-discord-rpc/tar.gz/4b20388e4b65e0efcff2aa9a8622b5884554cd8a: + resolution: {tarball: https://codeload.github.com/Youwes09/tauri-plugin-discord-rpc/tar.gz/4b20388e4b65e0efcff2aa9a8622b5884554cd8a} + version: 0.1.0 + tauri-plugin-drpc@1.0.3: resolution: {integrity: sha512-vl5dXhjKbl7+Nf9veW12usdmIUtZXwEf91SzxQPZlbRRJ/sjizbbQlnkUTtx6baJuGzz0KXXgP9xUhF39BdiXQ==} peerDependencies: @@ -1372,6 +1379,10 @@ snapshots: magic-string: 0.30.21 zimmerframe: 1.1.4 + tauri-plugin-discord-rpc-api@https://codeload.github.com/Youwes09/tauri-plugin-discord-rpc/tar.gz/4b20388e4b65e0efcff2aa9a8622b5884554cd8a: + dependencies: + '@tauri-apps/api': 2.10.1 + tauri-plugin-drpc@1.0.3(typescript@5.9.3): dependencies: typescript: 5.9.3 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c18211c..86090f1 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -268,9 +268,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.58" +version = "1.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" +checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283" dependencies = [ "find-msvc-tools", "shlex", @@ -699,6 +699,21 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "discord-rich-presence" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90c55d69cab17c19677ce3a5f8face993a9e6eaf847fecac3547f3a3ff4a2494" +dependencies = [ + "log", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "thiserror 2.0.18", + "uuid 0.8.2", +] + [[package]] name = "dispatch2" version = "0.3.1" @@ -2109,10 +2124,10 @@ dependencies = [ "dirs 5.0.1", "serde", "serde_json", - "sysinfo", + "sysinfo 0.32.1", "tauri", "tauri-build", - "tauri-plugin-drpc", + "tauri-plugin-discord-rpc", "tauri-plugin-http", "tauri-plugin-os", "tauri-plugin-process", @@ -2370,6 +2385,16 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "objc2-io-surface" version = "0.3.2" @@ -3341,19 +3366,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rpcdiscord" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71aa9a2097dc0176805e24debcb5d3ea5a17b796cd1d28e76b29f78fb49d7d5d" -dependencies = [ - "serde", - "serde_derive", - "serde_json", - "serde_repr", - "uuid 0.8.2", -] - [[package]] name = "rustc-hash" version = "2.1.2" @@ -3605,9 +3617,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" dependencies = [ "serde", "serde_core", @@ -4082,6 +4094,20 @@ dependencies = [ "windows 0.57.0", ] +[[package]] +name = "sysinfo" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows 0.61.3", +] + [[package]] name = "system-configuration" version = "0.7.0" @@ -4314,15 +4340,16 @@ dependencies = [ ] [[package]] -name = "tauri-plugin-drpc" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b291669b7dbc05471fba380eeecf31e3f733ae6013aaa5216a43ca376027e5a" +name = "tauri-plugin-discord-rpc" +version = "0.1.0" +source = "git+https://github.com/Youwes09/tauri-plugin-discord-rpc#d2fd312945d0573153e0e7e2d2dfb131acecc52c" dependencies = [ + "discord-rich-presence", + "libc", "log", - "rpcdiscord", "serde", "serde_json", + "sysinfo 0.36.1", "tauri", "tauri-plugin", "thiserror 2.0.18", @@ -4689,9 +4716,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.50.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd" dependencies = [ "bytes", "libc", @@ -4704,9 +4731,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9c909a1..170903f 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -15,22 +15,22 @@ path = "src/main.rs" tauri-build = { version = "2.0", features = [] } [dependencies] -tauri = { version = "2.0", features = [] } -tauri-plugin-shell = "2" -tauri-plugin-updater = "2" -tauri-plugin-process = "2" -tauri-plugin-http = "2" -serde = { version = "1", features = ["derive"] } -serde_json = "1" -walkdir = "2" -sysinfo = "0.32" -dirs = "5" -tauri-plugin-os = "2.3.2" -tauri-plugin-drpc = "0.1.6" +tauri = { version = "2.0", features = [] } +tauri-plugin-shell = "2" +tauri-plugin-updater = "2" +tauri-plugin-process = "2" +tauri-plugin-http = "2" +tauri-plugin-os = "2.3.2" +tauri-plugin-discord-rpc = { git = "https://github.com/Youwes09/tauri-plugin-discord-rpc" } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +walkdir = "2" +sysinfo = "0.32" +dirs = "5" [profile.release] codegen-units = 1 -lto = true -opt-level = "s" -panic = "abort" -strip = true +lto = true +opt-level = "s" +panic = "abort" +strip = true diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 6de83d1..015518a 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -33,11 +33,11 @@ "process:allow-restart", "http:default", "http:allow-fetch", - "drpc:default", - "drpc:allow-is-running", - "drpc:allow-spawn-thread", - "drpc:allow-destroy-thread", - "drpc:allow-set-activity", - "drpc:allow-clear-activity" + "discord-rpc:default", + "discord-rpc:allow-connect", + "discord-rpc:allow-disconnect", + "discord-rpc:allow-set-activity", + "discord-rpc:allow-clear-activity", + "discord-rpc:allow-is-running" ] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0eb945b..0d6fecf 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -26,9 +26,6 @@ pub enum SpawnError { SpawnFailed(String), } -// ── Update types ────────────────────────────────────────────────────────────── - -/// A single GitHub release returned to the frontend. #[derive(Serialize, Clone)] pub struct ReleaseInfo { pub tag_name: String, @@ -38,7 +35,6 @@ pub struct ReleaseInfo { pub html_url: String, } -/// Progress event emitted during download — matches what the frontend listens for. #[derive(Clone, serde::Serialize)] #[cfg_attr(not(target_os = "windows"), allow(dead_code))] struct UpdateProgress { @@ -46,8 +42,6 @@ struct UpdateProgress { total: Option, } -/// Strip the \\?\ extended-length path prefix that Windows adds to long paths. -/// Java and many other tools do not accept this prefix and will fail silently. fn strip_unc(path: PathBuf) -> PathBuf { let s = path.to_string_lossy(); if let Some(stripped) = s.strip_prefix(r"\\?\") { @@ -61,10 +55,6 @@ fn resolve_downloads_path(downloads_path: &str) -> PathBuf { if !downloads_path.trim().is_empty() { return PathBuf::from(downloads_path); } - // Mirror Suwayomi-Server's own default: /Tachidesk/downloads - // Windows: %LOCALAPPDATA%\Tachidesk\downloads - // macOS: ~/Library/Application Support/Tachidesk/downloads - // Linux: $XDG_DATA_HOME/Tachidesk/downloads (~/.local/share/Tachidesk/downloads) let base = std::env::var("XDG_DATA_HOME") .map(PathBuf::from) .unwrap_or_else(|_| dirs::data_dir().unwrap_or_else(|| PathBuf::from("/"))); @@ -108,34 +98,23 @@ fn get_storage_info(downloads_path: String) -> Result { }) } -/// Returns the resolved default downloads path for the current platform. -/// This mirrors resolve_downloads_path("") so the frontend can display it. #[tauri::command] fn get_default_downloads_path() -> String { resolve_downloads_path("").to_string_lossy().into_owned() } -/// Returns true if the given path exists and is a directory. #[tauri::command] fn check_path_exists(path: String) -> bool { std::path::Path::new(path.trim()).is_dir() } -/// Creates a directory and all missing parent directories. #[tauri::command] fn create_directory(path: String) -> Result<(), String> { std::fs::create_dir_all(path.trim()).map_err(|e| e.to_string()) } -/// Moves all content from `src` into `dst`, then removes `src`. -/// Emits `migrate_progress` events: `{ done, total, current }`. -/// Only deletes the source tree after every file is confirmed copied. #[tauri::command] -async fn migrate_downloads( - app: tauri::AppHandle, - src: String, - dst: String, -) -> Result<(), String> { +async fn migrate_downloads(app: tauri::AppHandle, src: String, dst: String) -> Result<(), String> { use tauri::Emitter; use std::fs; @@ -143,19 +122,16 @@ async fn migrate_downloads( let dst_path = std::path::PathBuf::from(dst.trim()); if !src_path.is_dir() { - return Ok(()); // nothing to migrate + return Ok(()); } - // Count files first so the frontend can show accurate progress let total: u64 = WalkDir::new(&src_path) .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.file_type().is_file()) .count() as u64; - let _ = app.emit("migrate_progress", serde_json::json!({ - "done": 0u64, "total": total, "current": "" - })); + let _ = app.emit("migrate_progress", serde_json::json!({ "done": 0u64, "total": total, "current": "" })); let mut done: u64 = 0; @@ -172,23 +148,15 @@ async fn migrate_downloads( fs::copy(entry.path(), &target).map_err(|e| e.to_string())?; done += 1; let _ = app.emit("migrate_progress", serde_json::json!({ - "done": done, - "total": total, - "current": rel.to_string_lossy() + "done": done, "total": total, "current": rel.to_string_lossy() })); } } - // Only remove source after all files are confirmed copied fs::remove_dir_all(&src_path).map_err(|e| e.to_string())?; Ok(()) } -/// Returns the OS/monitor DPI scale factor for the window's current monitor. -/// This is the real hardware scale — 1.0 on standard displays, 2.0 on HiDPI/4K, -/// 1.25–1.5 on Windows displays with OS-level scaling applied. -/// The frontend multiplies this by the user's uiZoom preference to get the -/// final effective zoom applied to document.documentElement. #[tauri::command] fn get_platform_ui_scale(window: tauri::Window) -> f64 { window.scale_factor().unwrap_or(1.0) @@ -210,8 +178,6 @@ fn kill_tachidesk(app: &tauri::AppHandle) { .creation_flags(CREATE_NO_WINDOW) .status(); - // Poll until no java.exe remains (up to ~3 s) so the installer can - // overwrite the JRE DLLs without hitting a sharing-violation error. for _ in 0..30 { let still_running = std::process::Command::new("tasklist") .args(["/FI", "IMAGENAME eq java.exe", "/NH"]) @@ -220,20 +186,15 @@ fn kill_tachidesk(app: &tauri::AppHandle) { .map(|o| String::from_utf8_lossy(&o.stdout).contains("java.exe")) .unwrap_or(false); - if !still_running { - break; - } + if !still_running { break; } std::thread::sleep(std::time::Duration::from_millis(100)); } } #[cfg(not(target_os = "windows"))] - let _ = std::process::Command::new("pkill") - .args(["-f", "tachidesk"]) - .status(); + let _ = std::process::Command::new("pkill").args(["-f", "tachidesk"]).status(); } - const DEFAULT_SERVER_CONF: &str = r#"server.ip = "127.0.0.1" server.port = 4567 server.webUIEnabled = false @@ -328,23 +289,14 @@ struct ServerInvocation { working_dir: Option, } -/// Locate the `java` / `java.exe` binary inside a bundled JRE directory. -/// -/// Expected layout (Windows and Linux): -/// /jre/bin/java[.exe] -/// -/// On Windows `strip_unc` is applied so Java doesn't choke on `\\?\` prefixes. #[cfg(not(target_os = "macos"))] fn find_java_in_bundle(bundle_dir: &PathBuf, log: &mut Option) -> Option { #[cfg(target_os = "windows")] let java = strip_unc(bundle_dir.join("jre").join("bin").join("java.exe")); - #[cfg(not(target_os = "windows"))] let java = bundle_dir.join("jre").join("bin").join("java"); - do_log(log, &format!("[find_java] checking path: {:?}", java)); - do_log(log, &format!("[find_java] exists: {}", java.exists())); - + do_log(log, &format!("[find_java] path: {:?} exists: {}", java, java.exists())); if java.exists() { Some(java) } else { None } } @@ -360,12 +312,8 @@ fn resolve_server_binary( app: &tauri::AppHandle, log: &mut Option, ) -> Result { - do_log(log, &format!("[resolve] binary arg = {:?}", binary)); + do_log(log, &format!("[resolve] binary = {:?}", binary)); - // ── 1. User-specified binary path ───────────────────────────────────────── - // Primary: honour the path as-is (doc-2 behaviour — trust the user). - // Fallback: if the path doesn't exist after stripping UNC, log a warning - // and continue so the bundled detection still has a chance. if !binary.trim().is_empty() { let path = strip_unc(PathBuf::from(binary.trim())); do_log(log, &format!("[resolve] user path: {:?} exists={}", path, path.exists())); @@ -376,62 +324,44 @@ fn resolve_server_binary( working_dir: path.parent().map(|p| p.to_path_buf()), }); } - // Fallback: path was set but file is missing — warn and keep trying. - do_log(log, "[resolve] WARNING: user-supplied path not found, falling through to bundled detection"); + do_log(log, "[resolve] user path not found, falling through"); } - // Resolve and UNC-strip resource_dir once; used by all non-macOS branches. #[cfg(not(target_os = "macos"))] let resource_dir = { let raw = app.path().resource_dir().unwrap_or_default(); let stripped = strip_unc(raw); - do_log(log, &format!("[resolve] resource_dir (stripped) = {:?}", stripped)); + do_log(log, &format!("[resolve] resource_dir = {:?}", stripped)); stripped }; - // ── 2. Bundled JRE + JAR (Windows / Linux — specific layout) ───────────── - // Primary path from doc-2: binaries/suwayomi-bundle/bin/Suwayomi-Server.jar #[cfg(not(target_os = "macos"))] { let bundle_dir = resource_dir.join("binaries").join("suwayomi-bundle"); let jar = bundle_dir.join("bin").join("Suwayomi-Server.jar"); - do_log(log, &format!("[resolve] bundle_dir = {:?}", bundle_dir)); - do_log(log, &format!("[resolve] bundle_dir exists: {}", bundle_dir.exists())); - do_log(log, &format!("[resolve] jar = {:?}", jar)); - do_log(log, &format!("[resolve] jar exists: {}", jar.exists())); + do_log(log, &format!("[resolve] bundle_dir={:?} exists={}", bundle_dir, bundle_dir.exists())); + do_log(log, &format!("[resolve] jar={:?} exists={}", jar, jar.exists())); match find_java_in_bundle(&bundle_dir, log) { - Some(java) => { - do_log(log, &format!("[resolve] java found: {:?}", java)); - if jar.exists() { - do_log(log, "[resolve] both java and jar found — using bundled JRE"); - return Ok(ServerInvocation { - bin: java.to_string_lossy().into_owned(), - args: vec!["-jar".to_string(), jar.to_string_lossy().into_owned()], - working_dir: Some(bundle_dir), - }); - } - do_log(log, "[resolve] java found but jar MISSING — falling through"); - } - None => { - do_log(log, "[resolve] java NOT found in bundle — falling through"); + Some(java) if jar.exists() => { + do_log(log, "[resolve] using bundled JRE"); + return Ok(ServerInvocation { + bin: java.to_string_lossy().into_owned(), + args: vec!["-jar".to_string(), jar.to_string_lossy().into_owned()], + working_dir: Some(bundle_dir), + }); } + _ => do_log(log, "[resolve] bundled JRE/jar not found, falling through"), } } - // ── 2b. Bundled launcher scripts / native sidecars (Windows / Linux) ────── - // Fallback for older bundle layouts that ship a wrapper script instead of a - // bare JRE + JAR. Also scans for any *.jar in resource_dir as a last resort. #[cfg(not(target_os = "macos"))] { - // Named launcher scripts. - let script_candidates = ["suwayomi-launcher", "suwayomi-launcher.sh", "tachidesk-server"]; - for name in &script_candidates { + for name in &["suwayomi-launcher", "suwayomi-launcher.sh", "tachidesk-server"] { let p = resource_dir.join(name); - do_log(log, &format!("[resolve] sidecar candidate: {:?} exists={}", p, p.exists())); + do_log(log, &format!("[resolve] sidecar: {:?} exists={}", p, p.exists())); if p.exists() { - do_log(log, &format!("[resolve] using sidecar: {:?}", p)); return Ok(ServerInvocation { bin: p.to_string_lossy().into_owned(), args: vec![], @@ -440,50 +370,31 @@ fn resolve_server_binary( } } - // Generic JRE at resource_dir root + any *.jar alongside it. - do_log(log, "[resolve] no named sidecar found, trying generic JRE + any jar in resource_dir"); if let Some(java) = find_java_in_bundle(&resource_dir, log) { let jar = std::fs::read_dir(&resource_dir) .ok() .and_then(|mut rd| { - rd.find(|e| { - e.as_ref() - .map(|e| e.file_name().to_string_lossy().ends_with(".jar")) - .unwrap_or(false) - }) - .and_then(|e| e.ok()) - .map(|e| e.path()) + rd.find(|e| e.as_ref().map(|e| e.file_name().to_string_lossy().ends_with(".jar")).unwrap_or(false)) + .and_then(|e| e.ok()) + .map(|e| e.path()) }); - do_log(log, &format!("[resolve] generic jar candidate: {:?}", jar)); - if let Some(jar_path) = jar { - do_log(log, &format!("[resolve] using generic JRE java={:?} jar={:?}", java, jar_path)); + do_log(log, &format!("[resolve] generic JRE java={:?} jar={:?}", java, jar_path)); return Ok(ServerInvocation { bin: java.to_string_lossy().into_owned(), args: vec!["-jar".to_string(), jar_path.to_string_lossy().into_owned()], working_dir: Some(resource_dir), }); } - do_log(log, "[resolve] generic JRE found but no .jar — falling through"); } } - // ── 3. macOS app bundle — MacOS/ then Resources/ ────────────────────────── #[cfg(target_os = "macos")] { let resource_dir = app.path().resource_dir().unwrap_or_default(); - let macos_dir = resource_dir - .parent() - .map(|p| p.join("MacOS")) - .unwrap_or_default(); + let macos_dir = resource_dir.parent().map(|p| p.join("MacOS")).unwrap_or_default(); - do_log(log, &format!("[resolve] macOS macos_dir = {:?}", macos_dir)); - - // Tauri strips the target triple when installing externalBin sidecars into - // Contents/MacOS/, so the binary is "suwayomi-server" at runtime. - // Triple-suffixed names are kept as a belt-and-suspenders fallback for - // dev / flat layouts. let candidates = [ "suwayomi-server", "suwayomi-server-aarch64-apple-darwin", @@ -498,7 +409,6 @@ fn resolve_server_binary( let p = search_dir.join(name); do_log(log, &format!("[resolve] macOS candidate: {:?} exists={}", p, p.exists())); if p.exists() { - do_log(log, &format!("[resolve] using macOS sidecar: {:?}", p)); return Ok(ServerInvocation { bin: p.to_string_lossy().into_owned(), args: vec![], @@ -509,37 +419,17 @@ fn resolve_server_binary( } } - // ── 4. PATH fallback ────────────────────────────────────────────────────── - // Use `where` on Windows, `which` everywhere else. - do_log(log, "[resolve] trying PATH fallback"); for name in &["suwayomi-server", "tachidesk-server"] { #[cfg(target_os = "windows")] - let found = std::process::Command::new("where") - .arg(name) - .output() - .map(|o| o.status.success()) - .unwrap_or(false); - + let found = std::process::Command::new("where").arg(name).output().map(|o| o.status.success()).unwrap_or(false); #[cfg(not(target_os = "windows"))] - let found = std::process::Command::new("which") - .arg(name) - .output() - .map(|o| o.status.success()) - .unwrap_or(false); - - do_log(log, &format!("[resolve] PATH check {:?}: found={}", name, found)); + let found = std::process::Command::new("which").arg(name).output().map(|o| o.status.success()).unwrap_or(false); if found { - do_log(log, &format!("[resolve] using PATH binary: {}", name)); - return Ok(ServerInvocation { - bin: name.to_string(), - args: vec![], - working_dir: None, - }); + return Ok(ServerInvocation { bin: name.to_string(), args: vec![], working_dir: None }); } } - do_log(log, "[resolve] FAILED — no binary found anywhere"); Err(SpawnError::NotConfigured( "Server binary not found. Install Suwayomi-Server or set the path in Settings.".to_string(), )) @@ -555,50 +445,28 @@ fn spawn_server(binary: String, app: tauri::AppHandle) -> Result<(), SpawnError> } let data_dir = suwayomi_data_dir(); - let log_path = data_dir.join("moku-spawn.log"); let _ = std::fs::create_dir_all(&data_dir); - let mut log = std::fs::OpenOptions::new() - .create(true) - .append(true) - .open(&log_path) - .ok(); + let mut log = std::fs::OpenOptions::new().create(true).append(true).open(&log_path).ok(); - do_log(&mut log, ""); - do_log(&mut log, "========================================"); - do_log(&mut log, &format!("[spawn_server] called at {:?}", std::time::SystemTime::now())); - do_log(&mut log, &format!("[spawn_server] binary arg = {:?}", binary)); - do_log(&mut log, &format!("[spawn_server] data_dir = {:?}", data_dir)); - do_log(&mut log, &format!("[spawn_server] log file = {:?}", log_path)); - do_log(&mut log, &format!("[spawn_server] APPDATA = {:?}", std::env::var("APPDATA"))); - do_log(&mut log, &format!("[spawn_server] LOCALAPPDATA = {:?}", std::env::var("LOCALAPPDATA"))); - do_log(&mut log, &format!("[spawn_server] current_dir = {:?}", std::env::current_dir())); + do_log(&mut log, &format!("[spawn_server] binary={:?} data_dir={:?}", binary, data_dir)); seed_server_conf(&data_dir); - do_log(&mut log, "[spawn_server] server.conf seeded"); - let mut invocation = match resolve_server_binary(&binary, &app, &mut log) { - Ok(i) => i, - Err(e) => { - do_log(&mut log, &format!("[spawn_server] resolve FAILED: {:?}", e)); - return Err(e); - } - }; + let mut invocation = resolve_server_binary(&binary, &app, &mut log).map_err(|e| { + do_log(&mut log, &format!("[spawn_server] resolve failed: {:?}", e)); + e + })?; - let bin_display = invocation.bin.clone(); let rootdir_flag = format!( "-Dsuwayomi.tachidesk.config.server.rootDir={}", data_dir.to_string_lossy() ); - invocation.args.insert(0, rootdir_flag); - let working_dir = invocation.working_dir - .unwrap_or_else(|| std::env::current_dir().unwrap_or_default()); + let working_dir = invocation.working_dir.unwrap_or_else(|| std::env::current_dir().unwrap_or_default()); - do_log(&mut log, &format!("[spawn_server] bin = {:?}", bin_display)); - do_log(&mut log, &format!("[spawn_server] args = {:?}", invocation.args)); - do_log(&mut log, &format!("[spawn_server] working_dir = {:?}", working_dir)); + do_log(&mut log, &format!("[spawn_server] bin={:?} args={:?} cwd={:?}", invocation.bin, invocation.args, working_dir)); let cmd = app.shell() .command(&invocation.bin) @@ -606,17 +474,13 @@ fn spawn_server(binary: String, app: tauri::AppHandle) -> Result<(), SpawnError> .args(&invocation.args) .current_dir(&working_dir); - do_log(&mut log, "[spawn_server] calling cmd.spawn()..."); - match cmd.spawn() { Ok((_rx, child)) => { - do_log(&mut log, &format!("[spawn_server] SUCCESS — spawned: {}", bin_display)); *app.state::().0.lock().unwrap() = Some(child); Ok(()) } Err(e) => { - do_log(&mut log, &format!("[spawn_server] SPAWN FAILED: {}", e)); - do_log(&mut log, &format!("[spawn_server] error kind: {:?}", e)); + do_log(&mut log, &format!("[spawn_server] spawn failed: {}", e)); Err(SpawnError::SpawnFailed(e.to_string())) } } @@ -628,10 +492,6 @@ fn kill_server(app: tauri::AppHandle) -> Result<(), String> { Ok(()) } -// ── Update commands ─────────────────────────────────────────────────────────── - -/// Fetch the list of all GitHub releases so the frontend can show a version picker. -/// Uses tauri-plugin-http so it goes through Tauri's permission system. #[tauri::command] async fn list_releases() -> Result, String> { use tauri_plugin_http::reqwest; @@ -663,22 +523,15 @@ async fn list_releases() -> Result, String> { let body = resp.text().await.map_err(|e| e.to_string())?; let releases: Vec = serde_json::from_str(&body).map_err(|e| e.to_string())?; - Ok(releases - .into_iter() - .map(|r| ReleaseInfo { - tag_name: r.tag_name.clone(), - name: r.name.unwrap_or_else(|| r.tag_name.clone()), - body: r.body.unwrap_or_default(), - published_at: r.published_at.unwrap_or_default(), - html_url: r.html_url, - }) - .collect()) + Ok(releases.into_iter().map(|r| ReleaseInfo { + tag_name: r.tag_name.clone(), + name: r.name.unwrap_or_else(|| r.tag_name.clone()), + body: r.body.unwrap_or_default(), + published_at: r.published_at.unwrap_or_default(), + html_url: r.html_url, + }).collect()) } -/// Download and install the latest update using tauri-plugin-updater. -/// Emits `update-progress` events with `{ downloaded, total }` while downloading. -/// On Windows the installer runs in passive (silent) mode; the frontend prompts restart. -/// On other platforms this command is a no-op — the frontend opens the GitHub page instead. #[tauri::command] #[allow(unused_variables)] async fn download_and_install_update(app: tauri::AppHandle) -> Result<(), String> { @@ -693,7 +546,7 @@ async fn download_and_install_update(app: tauri::AppHandle) -> Result<(), String let update = updater.check().await.map_err(|e| e.to_string())?; let Some(update) = update else { - return Err("No update available from the updater endpoint.".into()); + return Err("No update available.".into()); }; let app_clone = app.clone(); @@ -711,18 +564,15 @@ async fn download_and_install_update(app: tauri::AppHandle) -> Result<(), String } } -/// Restart the app after a successful update install. #[tauri::command] fn restart_app(app: tauri::AppHandle) { tauri::process::restart(&app.env()); } -// ── App entry point ─────────────────────────────────────────────────────────── - #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() - .plugin(tauri_plugin_drpc::init()) + .plugin(tauri_plugin_discord_rpc::init()) .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_http::init()) diff --git a/src/lib/discord.ts b/src/lib/discord.ts index 275d9c7..0a245f9 100644 --- a/src/lib/discord.ts +++ b/src/lib/discord.ts @@ -1,79 +1,79 @@ -import { start, stop, setActivity, clearActivity } from "tauri-plugin-drpc"; -import { Activity, Assets, Button, Timestamps } from "tauri-plugin-drpc/activity"; -import type { Manga, Chapter } from "./types"; +import { connect, disconnect, setActivity, clearActivity } from "tauri-plugin-discord-rpc-api"; +import { listen } from '@tauri-apps/api/event' +import type { Manga, Chapter } from './types' -const APP_ID = "1487894643613106298"; -const FALLBACK_IMAGE = "moku_logo"; +const APP_ID = '1487894643613106298' +const FALLBACK_IMAGE = 'moku_logo' -let sessionStart: number | null = null; +let sessionStart: number | null = null +let unlisten: (() => void) | null = null function isPublicUrl(url: string | null | undefined): boolean { - return typeof url === "string" && url.startsWith("https://"); + return typeof url === 'string' && url.startsWith('https://') } function resolveCoverImage(manga: Manga): string { - return isPublicUrl(manga.thumbnailUrl) ? manga.thumbnailUrl : FALLBACK_IMAGE; + return isPublicUrl(manga.thumbnailUrl) ? manga.thumbnailUrl : FALLBACK_IMAGE } function trunc(s: string, max = 128): string { - return s.length <= max ? s : `${s.slice(0, max - 1)}…`; + return s.length <= max ? s : `${s.slice(0, max - 1)}…` } function formatChapter(chapter: Chapter): string { - const n = chapter.chapterNumber; - return `Chapter ${Number.isInteger(n) ? n : n.toFixed(1)}`; -} - -function getTimestamps(): Timestamps { - return new Timestamps(sessionStart ?? Date.now()); + const n = chapter.chapterNumber + return `Chapter ${Number.isInteger(n) ? n : n.toFixed(1)}` } const BUTTONS = [ - new Button("GitHub", "https://github.com/Youwes09/Moku"), - new Button("Discord", "https://discord.gg/Jq3pwuNqPp"), -]; + { label: 'GitHub', url: 'https://github.com/Youwes09/Moku' }, + { label: 'Discord', url: 'https://discord.gg/Jq3pwuNqPp' }, +] export async function initRpc(): Promise { - sessionStart = Date.now(); - await start(APP_ID).catch(() => {}); + sessionStart = Date.now() + + unlisten = await listen('discord-rpc://running', ({ payload }) => { + if (payload) setIdle().catch(() => {}) + }) + + await connect(APP_ID).catch(() => {}) } export async function setReading(manga: Manga, chapter: Chapter): Promise { - const assets = new Assets() - .setLargeImage(resolveCoverImage(manga)) - .setLargeText(trunc(manga.title)) - .setSmallImage(FALLBACK_IMAGE) - .setSmallText("Moku"); - - const activity = new Activity() - .setDetails(trunc(manga.title)) - .setState(`${formatChapter(chapter)} · Reading`) - .setAssets(assets) - .setTimestamps(getTimestamps()); - activity.setButton(BUTTONS); - - await setActivity(activity).catch(() => {}); + await setActivity({ + details: trunc(manga.title), + state: `${formatChapter(chapter)} · Reading`, + timestamps: { start: sessionStart ?? Date.now() }, + assets: { + largeImage: resolveCoverImage(manga), + largeText: trunc(manga.title), + smallImage: FALLBACK_IMAGE, + smallText: 'Moku', + }, + buttons: BUTTONS, + }).catch(() => {}) } export async function setIdle(): Promise { - const assets = new Assets() - .setLargeImage(FALLBACK_IMAGE) - .setLargeText("Moku"); - - const activity = new Activity() - .setDetails("Browsing") - .setAssets(assets) - .setTimestamps(getTimestamps()); - activity.setButton(BUTTONS); - - await setActivity(activity).catch(() => {}); + await setActivity({ + details: 'Browsing', + timestamps: { start: sessionStart ?? Date.now() }, + assets: { + largeImage: FALLBACK_IMAGE, + largeText: 'Moku', + }, + buttons: BUTTONS, + }).catch(() => {}) } export async function clearReading(): Promise { - await clearActivity().catch(() => {}); + await clearActivity().catch(() => {}) } export async function destroyRpc(): Promise { - sessionStart = null; - await stop().catch(() => {}); + unlisten?.() + unlisten = null + sessionStart = null + await disconnect().catch(() => {}) }