From c9019ca772483eec26faeafdd4a58e28eb30ba6a Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 22 May 2025 15:22:02 +0200 Subject: [PATCH 01/91] Svg component plugin examples --- .../plugins/ui_detail/gauntlet.toml | 11 ++++++++++- .../plugins/ui_detail/src/content-svg.tsx | 14 ++++++++++++++ example_plugins/plugins/ui_grid/gauntlet.toml | 11 ++++++++++- .../plugins/ui_grid/src/content-image.tsx | 7 ++++--- .../plugins/ui_grid/src/content-svg.tsx | 16 ++++++++++++++++ .../plugins/ui_inline_svg/.gitignore | 2 ++ .../plugins/ui_inline_svg/gauntlet.toml | 16 ++++++++++++++++ .../plugins/ui_inline_svg/package.json | 17 +++++++++++++++++ .../plugins/ui_inline_svg/src/main.tsx | 19 +++++++++++++++++++ .../plugins/ui_inline_svg/tsconfig.json | 11 +++++++++++ example_plugins/plugins/ui_list/gauntlet.toml | 12 ++++++++++-- .../plugins/ui_list/src/content-svg.tsx | 17 +++++++++++++++++ .../ui_detail/content-svg/default.json | 3 +++ .../ui_grid/content-svg/default.json | 3 +++ .../scenarios/ui_inline_svg/main/default.json | 4 ++++ .../ui_list/content-svg/default.json | 3 +++ 16 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 example_plugins/plugins/ui_detail/src/content-svg.tsx create mode 100644 example_plugins/plugins/ui_grid/src/content-svg.tsx create mode 100644 example_plugins/plugins/ui_inline_svg/.gitignore create mode 100644 example_plugins/plugins/ui_inline_svg/gauntlet.toml create mode 100644 example_plugins/plugins/ui_inline_svg/package.json create mode 100644 example_plugins/plugins/ui_inline_svg/src/main.tsx create mode 100644 example_plugins/plugins/ui_inline_svg/tsconfig.json create mode 100644 example_plugins/plugins/ui_list/src/content-svg.tsx create mode 100644 example_plugins/scenarios/ui_detail/content-svg/default.json create mode 100644 example_plugins/scenarios/ui_grid/content-svg/default.json create mode 100644 example_plugins/scenarios/ui_inline_svg/main/default.json create mode 100644 example_plugins/scenarios/ui_list/content-svg/default.json diff --git a/example_plugins/plugins/ui_detail/gauntlet.toml b/example_plugins/plugins/ui_detail/gauntlet.toml index b787a2e..05145da 100644 --- a/example_plugins/plugins/ui_detail/gauntlet.toml +++ b/example_plugins/plugins/ui_detail/gauntlet.toml @@ -56,6 +56,15 @@ type = 'view' description = '' # docs-code-segment:end +# docs-code-segment:start content-svg +[[entrypoint]] +id = 'content-svg' +name = 'Content Svg' +path = 'src/content-svg.tsx' +type = 'view' +description = '' +# docs-code-segment:end + # docs-code-segment:start main [[entrypoint]] id = 'main' @@ -120,4 +129,4 @@ description = '' # docs-code-segment:end [permissions] -network = ["static.wikia.nocookie.net"] +network = ["static.wikia.nocookie.net", "upload.wikimedia.org"] diff --git a/example_plugins/plugins/ui_detail/src/content-svg.tsx b/example_plugins/plugins/ui_detail/src/content-svg.tsx new file mode 100644 index 0000000..092dff7 --- /dev/null +++ b/example_plugins/plugins/ui_detail/src/content-svg.tsx @@ -0,0 +1,14 @@ +import { ReactNode } from "react"; +import { Detail } from "@project-gauntlet/api/components"; + +const svgUrl = "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg" + +export default function ContentSvg(): ReactNode { + return ( + + + + + + ) +} diff --git a/example_plugins/plugins/ui_grid/gauntlet.toml b/example_plugins/plugins/ui_grid/gauntlet.toml index 9d7639b..a985b56 100644 --- a/example_plugins/plugins/ui_grid/gauntlet.toml +++ b/example_plugins/plugins/ui_grid/gauntlet.toml @@ -102,6 +102,15 @@ type = 'view' description = '' # docs-code-segment:end +# docs-code-segment:start content-svg +[[entrypoint]] +id = 'content-svg' +name = 'Grid Content Svg' +path = 'src/content-svg.tsx' +type = 'view' +description = '' +# docs-code-segment:end + # docs-code-segment:start focus [[entrypoint]] id = 'focus' @@ -112,4 +121,4 @@ description = '' # docs-code-segment:end [permissions] -network = ["static.wikia.nocookie.net"] +network = ["static.wikia.nocookie.net", "upload.wikimedia.org"] diff --git a/example_plugins/plugins/ui_grid/src/content-image.tsx b/example_plugins/plugins/ui_grid/src/content-image.tsx index 2e14906..953464f 100644 --- a/example_plugins/plugins/ui_grid/src/content-image.tsx +++ b/example_plugins/plugins/ui_grid/src/content-image.tsx @@ -1,15 +1,16 @@ import { ReactElement } from "react"; import { Grid } from "@project-gauntlet/api/components"; +const url = "https://static.wikia.nocookie.net/star-wars-canon/images/b/b0/Tatooine_TPM.png/revision/latest/scale-to-width-down/150?cb=20151124205032"; + export default function ContentImageExample(): ReactElement { return ( - + ) -} \ No newline at end of file +} diff --git a/example_plugins/plugins/ui_grid/src/content-svg.tsx b/example_plugins/plugins/ui_grid/src/content-svg.tsx new file mode 100644 index 0000000..73ef2af --- /dev/null +++ b/example_plugins/plugins/ui_grid/src/content-svg.tsx @@ -0,0 +1,16 @@ +import { ReactElement } from "react"; +import { Grid } from "@project-gauntlet/api/components"; + +const svgUrl = "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg" + +export default function ContentSvgExample(): ReactElement { + return ( + + + + + + + + ) +} diff --git a/example_plugins/plugins/ui_inline_svg/.gitignore b/example_plugins/plugins/ui_inline_svg/.gitignore new file mode 100644 index 0000000..de4d1f0 --- /dev/null +++ b/example_plugins/plugins/ui_inline_svg/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/example_plugins/plugins/ui_inline_svg/gauntlet.toml b/example_plugins/plugins/ui_inline_svg/gauntlet.toml new file mode 100644 index 0000000..a357d96 --- /dev/null +++ b/example_plugins/plugins/ui_inline_svg/gauntlet.toml @@ -0,0 +1,16 @@ +[gauntlet] +name = 'Docs Inline Svg' +description = '' + +# docs-code-segment:start main +[[entrypoint]] +id = 'main' +name = 'Main' +path = 'src/main.tsx' +type = 'inline-view' +description = '' +# docs-code-segment:end + +[permissions] +main_search_bar = ["read"] +network = ["upload.wikimedia.org"] diff --git a/example_plugins/plugins/ui_inline_svg/package.json b/example_plugins/plugins/ui_inline_svg/package.json new file mode 100644 index 0000000..3435221 --- /dev/null +++ b/example_plugins/plugins/ui_inline_svg/package.json @@ -0,0 +1,17 @@ +{ + "name": "@project-gauntlet/docs-inline-svg", + "private": true, + "scripts": { + "build": "gauntlet build", + "dev": "gauntlet dev" + }, + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@types/react": "*", + "@types/deno": "*", + "@project-gauntlet/tools": "*", + "typescript": "*" + } +} diff --git a/example_plugins/plugins/ui_inline_svg/src/main.tsx b/example_plugins/plugins/ui_inline_svg/src/main.tsx new file mode 100644 index 0000000..3da5592 --- /dev/null +++ b/example_plugins/plugins/ui_inline_svg/src/main.tsx @@ -0,0 +1,19 @@ +import { ReactElement } from "react"; +import { Inline } from "@project-gauntlet/api/components"; + +const svgUrl = "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg" + +export default function Main({ text }: { text: string }): ReactElement | null { + + if (text != "example") { + return null + } + + return ( + + + + + + ) +} diff --git a/example_plugins/plugins/ui_inline_svg/tsconfig.json b/example_plugins/plugins/ui_inline_svg/tsconfig.json new file mode 100644 index 0000000..f9bb627 --- /dev/null +++ b/example_plugins/plugins/ui_inline_svg/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "strict": true, + "module": "ES2022", + "esModuleInterop": true, + "target": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx" + }, + "lib": ["ES2020"] +} \ No newline at end of file diff --git a/example_plugins/plugins/ui_list/gauntlet.toml b/example_plugins/plugins/ui_list/gauntlet.toml index 4ce5eda..2297709 100644 --- a/example_plugins/plugins/ui_list/gauntlet.toml +++ b/example_plugins/plugins/ui_list/gauntlet.toml @@ -120,6 +120,15 @@ type = 'view' description = '' # docs-code-segment:end +# docs-code-segment:start content-svg +[[entrypoint]] +id = 'content-svg' +name = 'List Content Svg' +path = 'src/content-svg.tsx' +type = 'view' +description = '' +# docs-code-segment:end + # docs-code-segment:start metadata [[entrypoint]] id = 'metadata' @@ -193,5 +202,4 @@ description = '' # docs-code-segment:end [permissions] -network = ["static.wikia.nocookie.net"] - +network = ["static.wikia.nocookie.net", "upload.wikimedia.org"] diff --git a/example_plugins/plugins/ui_list/src/content-svg.tsx b/example_plugins/plugins/ui_list/src/content-svg.tsx new file mode 100644 index 0000000..d70e9a0 --- /dev/null +++ b/example_plugins/plugins/ui_list/src/content-svg.tsx @@ -0,0 +1,17 @@ +import { ReactElement } from "react"; +import { List } from "@project-gauntlet/api/components"; + +const svgUrl = "https://upload.wikimedia.org/wikipedia/commons/8/84/Example.svg" + +export default function ContentSvgExample(): ReactElement { + return ( + + + + + + + + + ) +} diff --git a/example_plugins/scenarios/ui_detail/content-svg/default.json b/example_plugins/scenarios/ui_detail/content-svg/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_detail/content-svg/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_grid/content-svg/default.json b/example_plugins/scenarios/ui_grid/content-svg/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_grid/content-svg/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_inline_svg/main/default.json b/example_plugins/scenarios/ui_inline_svg/main/default.json new file mode 100644 index 0000000..d880710 --- /dev/null +++ b/example_plugins/scenarios/ui_inline_svg/main/default.json @@ -0,0 +1,4 @@ +{ + "type": "Search", + "text": "example" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/content-svg/default.json b/example_plugins/scenarios/ui_list/content-svg/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/content-svg/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file From 7a47cdb9414220dda4f6cc64f5dcc57d075d6783 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 22 May 2025 15:22:59 +0200 Subject: [PATCH 02/91] Change screenshot generator theme to macos dark --- rust/server/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 5a87269..db20d80 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -136,7 +136,7 @@ fn run_scenario_runner() { std::thread::spawn(|| { let theme = crate::plugins::theme::BundledThemes::new().unwrap(); - start_mock_server(frontend_sender, backend_receiver, theme.legacy_theme) + start_mock_server(frontend_sender, backend_receiver, theme.macos_dark_theme) }); start_client(false, frontend_receiver, backend_sender); From 2a80b51ff5b1c3e47769b6a189074fc43a51858f Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 22 May 2025 20:34:39 +0200 Subject: [PATCH 03/91] Update package-lock.json --- package-lock.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/package-lock.json b/package-lock.json index ee9e48e..6c456b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -476,6 +476,22 @@ "resolved": "example_plugins/js/api", "link": true }, + "example_plugins/plugins/ui_inline_svg": { + "name": "@project-gauntlet/docs-inline-svg", + "dependencies": { + "@project-gauntlet/api": "file:../../js/api" + }, + "devDependencies": { + "@project-gauntlet/tools": "*", + "@types/deno": "*", + "@types/react": "*", + "typescript": "*" + } + }, + "example_plugins/plugins/ui_inline_svg/node_modules/@project-gauntlet/api": { + "resolved": "example_plugins/js/api", + "link": true + }, "example_plugins/plugins/ui_inline_three_sections": { "name": "@project-gauntlet/docs-inline-three-sections", "dependencies": { @@ -1290,6 +1306,10 @@ "resolved": "example_plugins/plugins/ui_inline_separator", "link": true }, + "node_modules/@project-gauntlet/docs-inline-svg": { + "resolved": "example_plugins/plugins/ui_inline_svg", + "link": true + }, "node_modules/@project-gauntlet/docs-inline-three-sections": { "resolved": "example_plugins/plugins/ui_inline_three_sections", "link": true From 3599837033c75c13946bbdc0ba8f011f34c94328 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 23 May 2025 20:59:04 +0200 Subject: [PATCH 04/91] Split plugin runtime (specifically deno) into separate workspace, and include it via --extern rustc flag --- .cargo/config.toml | 5 + Cargo.lock | 4980 +-------- Cargo.toml | 8 +- js/build/src/main.ts | 10 +- js/scenario_runner_cli/src/main.ts | 6 +- rust/common_plugin_runtime/Cargo.toml | 20 + .../src/api.rs | 2 +- rust/common_plugin_runtime/src/lib.rs | 100 + rust/common_plugin_runtime/src/model.rs | 213 + rust/plugin_runtime/.gitignore | 1 + rust/plugin_runtime/Cargo.lock | 9259 +++++++++++++++++ rust/plugin_runtime/Cargo.toml | 48 +- rust/plugin_runtime/src/assets.rs | 5 +- rust/plugin_runtime/src/clipboard.rs | 10 +- rust/plugin_runtime/src/deno.rs | 8 +- .../src/entrypoint_generators.rs | 5 +- rust/plugin_runtime/src/events.rs | 67 +- rust/plugin_runtime/src/lib.rs | 84 +- rust/plugin_runtime/src/model.rs | 150 +- rust/plugin_runtime/src/permissions.rs | 9 +- rust/plugin_runtime/src/preferences.rs | 7 +- rust/plugin_runtime/src/search.rs | 8 +- rust/plugin_runtime/src/ui.rs | 6 +- rust/server/Cargo.toml | 6 +- rust/server/src/lib.rs | 4 +- .../src/plugins/binary_data_gatherer.rs | 2 +- rust/server/src/plugins/clipboard.rs | 2 +- rust/server/src/plugins/js.rs | 44 +- rust/server/src/plugins/loader.rs | 2 +- rust/server/src/plugins/mod.rs | 9 +- 30 files changed, 9826 insertions(+), 5254 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 rust/common_plugin_runtime/Cargo.toml rename rust/{plugin_runtime => common_plugin_runtime}/src/api.rs (98%) create mode 100644 rust/common_plugin_runtime/src/lib.rs create mode 100644 rust/common_plugin_runtime/src/model.rs create mode 100644 rust/plugin_runtime/.gitignore create mode 100644 rust/plugin_runtime/Cargo.lock diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..67d4afc --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[build] +rustflags = """ +--extern gauntlet_plugin_runtime=./rust/plugin_runtime/target/release/libgauntlet_plugin_runtime.rlib +-L dependency=./rust/plugin_runtime/target/release/deps +""" diff --git a/Cargo.lock b/Cargo.lock index f763ee9..6212f61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,16 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "ab_glyph" version = "0.2.29" @@ -37,82 +27,12 @@ dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aead-gcm-stream" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4947a169074c7e038fa43051d1c4e073f4488b0e4b0a30658f1e1a1b06449ce8" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aes-kw" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" -dependencies = [ - "aes", -] - [[package]] name = "ahash" version = "0.7.8" @@ -152,21 +72,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - [[package]] name = "allocator-api2" version = "0.2.21" @@ -335,9 +240,6 @@ name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -dependencies = [ - "serde", -] [[package]] name = "as-raw-xcb-connection" @@ -345,15 +247,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" -[[package]] -name = "ash" -version = "0.37.3+1.3.251" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" -dependencies = [ - "libloading 0.7.4", -] - [[package]] name = "ash" version = "0.38.0+1.3.281" @@ -363,57 +256,6 @@ dependencies = [ "libloading 0.8.6", ] -[[package]] -name = "asn1-rs" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom 7.1.3", - "num-traits", - "rusticata-macros", - "thiserror 1.0.69", - "time", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure 0.12.6", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ast_node" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9184f2b369b3e8625712493c89b785881f27eedc6cde480a81883cef78868b2" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - [[package]] name = "async-broadcast" version = "0.7.1" @@ -438,20 +280,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-compression" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" -dependencies = [ - "brotli 7.0.0", - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -] - [[package]] name = "async-executor" version = "1.13.1" @@ -631,19 +459,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "attohttpc" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "184f5e6cce583a9db6b6f8d772a42cfce5b78e7c3ef26118cec3ce4c8c14969b" -dependencies = [ - "http 1.2.0", - "log", - "rustls 0.22.4", - "url", - "webpki-roots", -] - [[package]] name = "auto-launch" version = "0.5.0" @@ -652,7 +467,7 @@ checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471" dependencies = [ "dirs 4.0.0", "thiserror 1.0.69", - "winreg 0.10.1", + "winreg", ] [[package]] @@ -670,7 +485,7 @@ dependencies = [ "anyhow", "arrayvec", "log", - "nom 7.1.3", + "nom", "num-rational", "v_frame", ] @@ -694,8 +509,8 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.2.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "itoa", "matchit", @@ -720,8 +535,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.2.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", @@ -740,88 +555,24 @@ dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide 0.8.2", + "miniz_oxide", "object", "rustc-demangle", "windows-targets 0.52.6", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base32" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64-simd" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" -dependencies = [ - "simd-abstraction", -] - -[[package]] -name = "base64-simd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" -dependencies = [ - "outref 0.5.1", - "vsimd", -] - [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "basic-toml" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" -dependencies = [ - "serde", -] - -[[package]] -name = "better_scoped_tls" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297b153aa5e573b5863108a6ddc9d5c968bd0b20e75cc614ee9821d2f45679c7" -dependencies = [ - "scoped-tls", -] - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bincode" version = "2.0.0-rc.3" @@ -841,50 +592,15 @@ dependencies = [ "virtue", ] -[[package]] -name = "bindgen" -version = "0.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" -dependencies = [ - "bitflags 2.6.0", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.101", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec 0.6.3", -] - [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec 0.8.0", + "bit-vec", ] -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bit-vec" version = "0.8.0" @@ -927,27 +643,6 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - [[package]] name = "block" version = "0.1.6" @@ -963,15 +658,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - [[package]] name = "block2" version = "0.5.1" @@ -994,48 +680,6 @@ dependencies = [ "piper", ] -[[package]] -name = "boxed_error" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" -dependencies = [ - "quote", - "syn 2.0.101", -] - -[[package]] -name = "brotli" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - [[package]] name = "bstr" version = "1.11.1" @@ -1057,9 +701,6 @@ name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -dependencies = [ - "allocator-api2", -] [[package]] name = "by_address" @@ -1105,30 +746,6 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" -[[package]] -name = "cacao" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5952f0958672e4aa8fc706d01905c56af57759e078c53a6fddf4a13361943e7a" -dependencies = [ - "block", - "core-foundation 0.9.4", - "core-graphics 0.22.3", - "dispatch", - "lazy_static", - "libc", - "objc", - "objc_id", - "os_info", - "url", -] - -[[package]] -name = "cache_control" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2a5fb3207c12b5d208ebc145f967fea5cac41a021c37417ccc31ba40f39ee" - [[package]] name = "cairo-rs" version = "0.18.5" @@ -1231,21 +848,12 @@ checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" dependencies = [ "camino", "cargo-platform", - "semver 1.0.24", + "semver", "serde", "serde_json", "thiserror 2.0.8", ] -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" version = "1.2.5" @@ -1269,15 +877,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom 7.1.3", -] - [[package]] name = "cfg-expr" version = "0.15.8" @@ -1316,32 +915,10 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", - "serde", "wasm-bindgen", "windows-targets 0.52.6", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading 0.8.6", -] - [[package]] name = "clap" version = "4.5.23" @@ -1431,27 +1008,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "color-print" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" -dependencies = [ - "color-print-proc-macro", -] - -[[package]] -name = "color-print-proc-macro" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" -dependencies = [ - "nom 7.1.3", - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "color_quant" version = "1.1.0" @@ -1489,26 +1045,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", -] - [[package]] name = "convert_case" version = "0.6.0" @@ -1527,12 +1063,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "cooked-waker" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" - [[package]] name = "core-foundation" version = "0.9.4" @@ -1559,19 +1089,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types 0.3.2", - "libc", -] - [[package]] name = "core-graphics" version = "0.23.2" @@ -1581,7 +1098,7 @@ dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", "core-graphics-types 0.1.3", - "foreign-types 0.5.0", + "foreign-types", "libc", ] @@ -1594,7 +1111,7 @@ dependencies = [ "bitflags 2.6.0", "core-foundation 0.10.0", "core-graphics-types 0.2.0", - "foreign-types 0.5.0", + "foreign-types", "libc", ] @@ -1620,20 +1137,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cosmic-protocols" -version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-protocols.git#d218c76b58c7a3b20dd5e7943f93fc306a1b81b8" -dependencies = [ - "bitflags 2.6.0", - "wayland-backend", - "wayland-client", - "wayland-protocols 0.32.5", - "wayland-protocols-wlr 0.3.5", - "wayland-scanner", - "wayland-server", -] - [[package]] name = "cosmic-text" version = "0.12.1" @@ -1739,18 +1242,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -1758,7 +1249,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", "typenum", ] @@ -1768,59 +1258,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - [[package]] name = "cursor-icon" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto 0.2.9", - "rustc_version 0.4.1", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "d3d12" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" -dependencies = [ - "bitflags 2.6.0", - "libloading 0.8.6", - "winapi", -] - [[package]] name = "dark-light" version = "1.1.1" @@ -1831,9 +1274,9 @@ dependencies = [ "detect-desktop-environment", "dirs 4.0.0", "objc", - "rust-ini 0.18.0", + "rust-ini", "web-sys", - "winreg 0.10.1", + "winreg", "zbus", ] @@ -1872,25 +1315,6 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core 0.9.10", -] - -[[package]] -name = "data-encoding" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - [[package]] name = "data-url" version = "0.3.0" @@ -1903,907 +1327,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7046468a81e6a002061c01e6a7c83139daf91b11c30e66795b13217c2d885c8b" -[[package]] -name = "debugid" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" -dependencies = [ - "serde", - "uuid", -] - -[[package]] -name = "deflate" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" -dependencies = [ - "adler32", - "byteorder", -] - -[[package]] -name = "deno_ast" -version = "0.43.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d00b724e06d2081a141ec1155756a0b465d413d8e2a7515221f61d482eb2ee" -dependencies = [ - "base64 0.21.7", - "deno_media_type", - "deno_terminal 0.1.1", - "dprint-swc-ext", - "once_cell", - "percent-encoding", - "serde", - "sourcemap 9.1.2", - "swc_atoms", - "swc_common", - "swc_config", - "swc_config_macro", - "swc_ecma_ast", - "swc_ecma_codegen", - "swc_ecma_codegen_macros", - "swc_ecma_loader", - "swc_ecma_parser", - "swc_ecma_transforms_base", - "swc_ecma_transforms_classes", - "swc_ecma_transforms_macros", - "swc_ecma_transforms_proposal", - "swc_ecma_transforms_react", - "swc_ecma_transforms_typescript", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_eq_ignore_macros", - "swc_macros_common", - "swc_visit", - "swc_visit_macros", - "text_lines", - "thiserror 1.0.69", - "unicode-width", - "url", -] - -[[package]] -name = "deno_broadcast_channel" -version = "0.173.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348ecdacfdd262e6b2f9740d07a41e8f4d79d06a670378a060515d0208495c9f" -dependencies = [ - "async-trait", - "deno_core", - "thiserror 1.0.69", - "tokio", - "uuid", -] - -[[package]] -name = "deno_cache" -version = "0.111.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6e35cb122e56c22149652327c90c563790ddcef24ea1fc77454e193131318e" -dependencies = [ - "async-trait", - "deno_core", - "rusqlite", - "serde", - "sha2", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "deno_canvas" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bbfd1437bc01ab775b1a60e3061bbf2e9517e31fb5eedf89b2b703104c835e6" -dependencies = [ - "deno_core", - "deno_webgpu", - "image 0.24.9", - "serde", - "thiserror 1.0.69", -] - -[[package]] -name = "deno_console" -version = "0.179.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e09f2bbb2d842329b602da25dbab5cd4a342f9a8adcb7c02509fc322f796e79" -dependencies = [ - "deno_core", -] - -[[package]] -name = "deno_core" -version = "0.321.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd2a54cda74cdc187d5fc2d23370a45cf09f912caf566dd1cd24a50157d809c7" -dependencies = [ - "anyhow", - "bincode 1.3.3", - "bit-set 0.5.3", - "bit-vec 0.6.3", - "bytes", - "cooked-waker", - "deno_core_icudata", - "deno_ops", - "deno_unsync", - "futures", - "indexmap 2.7.0", - "libc", - "memoffset", - "parking_lot 0.12.3", - "percent-encoding", - "pin-project", - "serde", - "serde_json", - "serde_v8", - "smallvec", - "sourcemap 8.0.1", - "static_assertions", - "tokio", - "url", - "v8", - "wasm_dep_analyzer", -] - -[[package]] -name = "deno_core_icudata" -version = "0.74.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" - -[[package]] -name = "deno_cron" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f936f036e9e3f88205db8efd0ec68c65efb47bc0cbe4b715bafecd6e9c407931" -dependencies = [ - "anyhow", - "async-trait", - "chrono", - "deno_core", - "saffron", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "deno_crypto" -version = "0.193.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b582f30887c7c0902b4445c64d7c8b98d0043ec547c44de8de26104b093e1be" -dependencies = [ - "aes", - "aes-gcm", - "aes-kw", - "base64 0.21.7", - "cbc", - "const-oid", - "ctr", - "curve25519-dalek", - "deno_core", - "deno_web", - "ed448-goldilocks", - "elliptic-curve", - "num-traits", - "once_cell", - "p256", - "p384", - "p521", - "rand", - "ring", - "rsa", - "sec1", - "serde", - "serde_bytes", - "sha1", - "sha2", - "signature", - "spki", - "thiserror 1.0.69", - "tokio", - "uuid", - "x25519-dalek", -] - -[[package]] -name = "deno_fetch" -version = "0.203.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18e66bd3bf786e24a8b8bdc97049fa82957b095a5fd1e142545c5a7cdd2272a" -dependencies = [ - "base64 0.21.7", - "bytes", - "data-url", - "deno_core", - "deno_permissions", - "deno_tls", - "dyn-clone", - "error_reporter", - "hickory-resolver", - "http 1.2.0", - "http-body-util", - "hyper 1.5.2", - "hyper-rustls", - "hyper-util", - "ipnet", - "percent-encoding", - "rustls-webpki", - "serde", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tokio-rustls", - "tokio-socks", - "tokio-util", - "tower 0.4.13", - "tower-http", - "tower-service", -] - -[[package]] -name = "deno_ffi" -version = "0.166.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6d2f13ebfa93833446abeb3bd1836fdf86bcb96678276b21a0622146f42284" -dependencies = [ - "deno_core", - "deno_permissions", - "dlopen2 0.6.1", - "dynasmrt", - "libffi", - "libffi-sys", - "log", - "num-bigint", - "serde", - "serde-value", - "serde_json", - "thiserror 1.0.69", - "tokio", - "winapi", -] - -[[package]] -name = "deno_fs" -version = "0.89.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f53829328c344736d7fdda44733057299536f3379513cdcd258823ef273540ec" -dependencies = [ - "async-trait", - "base32", - "boxed_error", - "deno_core", - "deno_io", - "deno_path_util", - "deno_permissions", - "filetime", - "junction", - "libc", - "nix 0.27.1", - "rand", - "rayon", - "serde", - "thiserror 1.0.69", - "winapi", - "windows-sys 0.52.0", -] - -[[package]] -name = "deno_http" -version = "0.177.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42b4ee6dbac20aa287a416f8905ed64b95cb484063c2af6be4eb232382c7fcb6" -dependencies = [ - "async-compression", - "async-trait", - "base64 0.21.7", - "brotli 6.0.0", - "bytes", - "cache_control", - "deno_core", - "deno_net", - "deno_websocket", - "flate2", - "http 0.2.12", - "http 1.2.0", - "httparse", - "hyper 0.14.32", - "hyper 1.5.2", - "hyper-util", - "itertools 0.10.5", - "memmem", - "mime", - "once_cell", - "percent-encoding", - "phf", - "pin-project", - "ring", - "scopeguard", - "serde", - "smallvec", - "thiserror 1.0.69", - "tokio", - "tokio-util", -] - -[[package]] -name = "deno_io" -version = "0.89.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc19195805a6b256d5ffe697c81ac79f8acd22246616fe880d6c9ec2dacf9bb4" -dependencies = [ - "async-trait", - "deno_core", - "filetime", - "fs3", - "libc", - "log", - "once_cell", - "os_pipe", - "parking_lot 0.12.3", - "pin-project", - "rand", - "tokio", - "uuid", - "winapi", - "windows-sys 0.52.0", -] - -[[package]] -name = "deno_kv" -version = "0.87.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a25347cd7ae561d0b05c24eebb3047e85a3af3f398675d5a9894fd167f2714f" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.21.7", - "boxed_error", - "bytes", - "chrono", - "deno_core", - "deno_fetch", - "deno_path_util", - "deno_permissions", - "deno_tls", - "denokv_proto", - "denokv_remote", - "denokv_sqlite", - "faster-hex", - "http 1.2.0", - "http-body-util", - "log", - "num-bigint", - "prost", - "prost-build", - "rand", - "rusqlite", - "serde", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "deno_media_type" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa135b8a9febc9a51c16258e294e268a1276750780d69e46edb31cced2826e4" -dependencies = [ - "data-url", - "serde", - "url", -] - -[[package]] -name = "deno_napi" -version = "0.110.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea57b67488969f82594cb008fed1bd99830e6db042e31ee9878933d8c76be41c" -dependencies = [ - "deno_core", - "deno_permissions", - "libc", - "libloading 0.7.4", - "log", - "napi_sym", - "thiserror 1.0.69", - "windows-sys 0.52.0", -] - -[[package]] -name = "deno_native_certs" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86bc737e098a45aa5742d51ce694ac7236a1e69fb0d9df8c862e9b4c9583c5f9" -dependencies = [ - "dlopen2 0.7.0", - "dlopen2_derive", - "once_cell", - "rustls-native-certs", - "rustls-pemfile", -] - -[[package]] -name = "deno_net" -version = "0.171.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b3a51f7b4d5d64d17a7bc6f7495498f20d809930979d21a059d75e850cdea6" -dependencies = [ - "deno_core", - "deno_permissions", - "deno_tls", - "hickory-proto", - "hickory-resolver", - "pin-project", - "rustls-tokio-stream", - "serde", - "socket2", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "deno_node" -version = "0.116.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd0d1a757f75224e84ce8a553c2465e4a352fba4b7551ec15809d8a119847e7" -dependencies = [ - "aead-gcm-stream", - "aes", - "async-trait", - "base64 0.21.7", - "blake2", - "boxed_error", - "brotli 6.0.0", - "bytes", - "cbc", - "const-oid", - "data-encoding", - "deno_core", - "deno_fetch", - "deno_fs", - "deno_io", - "deno_media_type", - "deno_net", - "deno_package_json", - "deno_path_util", - "deno_permissions", - "deno_whoami", - "der", - "digest", - "dsa", - "ecb", - "ecdsa", - "ed25519-dalek", - "elliptic-curve", - "errno 0.2.8", - "faster-hex", - "h2 0.4.7", - "hkdf", - "home", - "http 1.2.0", - "http-body-util", - "hyper 1.5.2", - "hyper-util", - "idna", - "indexmap 2.7.0", - "ipnetwork", - "k256", - "lazy-regex", - "libc", - "libz-sys", - "md-5", - "md4", - "memchr", - "node_resolver", - "num-bigint", - "num-bigint-dig", - "num-integer", - "num-traits", - "once_cell", - "p224", - "p256", - "p384", - "path-clean", - "pbkdf2", - "pin-project-lite", - "pkcs8", - "rand", - "regex", - "ring", - "ripemd", - "rsa", - "scrypt", - "sec1", - "serde", - "sha1", - "sha2", - "sha3", - "signature", - "simd-json", - "sm3", - "spki", - "stable_deref_trait", - "thiserror 1.0.69", - "tokio", - "tokio-eld", - "url", - "webpki-root-certs", - "winapi", - "windows-sys 0.52.0", - "x25519-dalek", - "x509-parser", - "yoke", -] - -[[package]] -name = "deno_ops" -version = "0.197.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a8825d92301cf445727c43f17fee2a20fcdf4370004339965156ae7c56c97e" -dependencies = [ - "proc-macro-rules", - "proc-macro2", - "quote", - "stringcase", - "strum", - "strum_macros", - "syn 2.0.101", - "thiserror 1.0.69", -] - -[[package]] -name = "deno_package_json" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbc4c4d3eb0960b58e8f43f9fc2d3f620fcac9a03cd85203e08db5b04e83c1f" -dependencies = [ - "deno_semver", - "indexmap 2.7.0", - "serde", - "serde_json", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "deno_path_util" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff25f6e08e7a0214bbacdd6f7195c7f1ebcd850c87a624e4ff06326b68b42d99" -dependencies = [ - "percent-encoding", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "deno_permissions" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e822f98185ab3ddf06104b2407681e0008af52361af32f1cd171b7eda5aa59" -dependencies = [ - "deno_core", - "deno_path_util", - "deno_terminal 0.2.0", - "fqdn", - "libc", - "log", - "once_cell", - "percent-encoding", - "serde", - "thiserror 1.0.69", - "which 4.4.2", - "winapi", -] - -[[package]] -name = "deno_runtime" -version = "0.188.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516ed4f796ab0f5dc092b5592ed6159c759f4f3a94f4a23455fecc94edc51dd1" -dependencies = [ - "async-trait", - "color-print", - "deno_ast", - "deno_broadcast_channel", - "deno_cache", - "deno_canvas", - "deno_console", - "deno_core", - "deno_cron", - "deno_crypto", - "deno_fetch", - "deno_ffi", - "deno_fs", - "deno_http", - "deno_io", - "deno_kv", - "deno_napi", - "deno_net", - "deno_node", - "deno_path_util", - "deno_permissions", - "deno_terminal 0.2.0", - "deno_tls", - "deno_url", - "deno_web", - "deno_webgpu", - "deno_webidl", - "deno_websocket", - "deno_webstorage", - "dlopen2 0.6.1", - "encoding_rs", - "fastwebsockets", - "flate2", - "http 1.2.0", - "http-body-util", - "hyper 0.14.32", - "hyper 1.5.2", - "hyper-util", - "libc", - "log", - "netif", - "nix 0.27.1", - "node_resolver", - "notify", - "ntapi", - "once_cell", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-otlp", - "opentelemetry-semantic-conventions", - "opentelemetry_sdk", - "percent-encoding", - "pin-project", - "regex", - "rustyline", - "same-file", - "serde", - "signal-hook", - "signal-hook-registry", - "tempfile", - "thiserror 1.0.69", - "tokio", - "tokio-metrics", - "twox-hash", - "uuid", - "which 4.4.2", - "winapi", - "windows-sys 0.52.0", -] - -[[package]] -name = "deno_semver" -version = "0.5.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c957c6a57c38b7dde2315df0da0ec228911e56a74f185b108a488d0401841a67" -dependencies = [ - "monch", - "once_cell", - "serde", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "deno_terminal" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6337d4e7f375f8b986409a76fbeecfa4bd8a1343e63355729ae4befa058eaf" -dependencies = [ - "once_cell", - "termcolor", -] - -[[package]] -name = "deno_terminal" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daef12499e89ee99e51ad6000a91f600d3937fb028ad4918af76810c5bc9e0d5" -dependencies = [ - "once_cell", - "termcolor", -] - -[[package]] -name = "deno_tls" -version = "0.166.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688175eed35e7b3053ec114227894ef24786855405d8844058a48bffa997d85a" -dependencies = [ - "deno_core", - "deno_native_certs", - "rustls 0.23.20", - "rustls-pemfile", - "rustls-tokio-stream", - "rustls-webpki", - "serde", - "thiserror 1.0.69", - "tokio", - "webpki-roots", -] - -[[package]] -name = "deno_unsync" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d774fd83f26b24f0805a6ab8b26834a0d06ceac0db517b769b1e4633c96a2057" -dependencies = [ - "futures", - "parking_lot 0.12.3", - "tokio", -] - -[[package]] -name = "deno_url" -version = "0.179.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9a108794e505f2b07665e19ff336c1bcba6adcf7182c90c1d3a6c741d7fcd0" -dependencies = [ - "deno_core", - "thiserror 1.0.69", - "urlpattern", -] - -[[package]] -name = "deno_web" -version = "0.210.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7679087bcc41f7ae3385f8c12d43bc81cfc54cb9b1ef73983d20f5e39fa4e0da" -dependencies = [ - "async-trait", - "base64-simd 0.8.0", - "bytes", - "deno_core", - "deno_permissions", - "encoding_rs", - "flate2", - "futures", - "serde", - "thiserror 1.0.69", - "tokio", - "uuid", -] - -[[package]] -name = "deno_webgpu" -version = "0.146.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f78b73638be1552b31778e42267f4fb47e902f7b261bdb0f951ba2b1d6bfab" -dependencies = [ - "deno_core", - "raw-window-handle", - "serde", - "thiserror 1.0.69", - "tokio", - "wgpu-core 0.21.1", - "wgpu-types 0.20.0", -] - -[[package]] -name = "deno_webidl" -version = "0.179.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b55d845e3d64f8de7eff67aaa4b6fe1b23bbc2efe967c984f8c64c8dd85fad4" -dependencies = [ - "deno_core", -] - -[[package]] -name = "deno_websocket" -version = "0.184.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00407052c6524828f2708557c47059ba9b87874758416c66f47f5102ac68422" -dependencies = [ - "bytes", - "deno_core", - "deno_net", - "deno_permissions", - "deno_tls", - "fastwebsockets", - "h2 0.4.7", - "http 1.2.0", - "http-body-util", - "hyper 1.5.2", - "hyper-util", - "once_cell", - "rustls-tokio-stream", - "serde", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "deno_webstorage" -version = "0.174.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecaabbb1580d21811642f11cc12fe8599684efeb9398eaa998a3db8811e8edc" -dependencies = [ - "deno_core", - "deno_web", - "rusqlite", - "thiserror 1.0.69", -] - -[[package]] -name = "deno_whoami" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e75e4caa92b98a27f09c671d1399aee0f5970aa491b9a598523aac000a2192e3" -dependencies = [ - "libc", - "whoami", -] - -[[package]] -name = "denokv_proto" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ba1f99ed11a9c11e868a8521b1f71a7e1aba785d7f42ea9ecbdc01146c89ec" -dependencies = [ - "anyhow", - "async-trait", - "chrono", - "futures", - "num-bigint", - "prost", - "serde", - "uuid", -] - -[[package]] -name = "denokv_remote" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ed833073189e8f6d03155fe3b05a024e75e29d8a28a4c2e9ec3b5c925e727b" -dependencies = [ - "anyhow", - "async-stream", - "async-trait", - "bytes", - "chrono", - "denokv_proto", - "futures", - "http 1.2.0", - "log", - "prost", - "rand", - "serde", - "serde_json", - "tokio", - "tokio-util", - "url", - "uuid", -] - -[[package]] -name = "denokv_sqlite" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b790f01d1302d53a0c3cbd27de88a06b3abd64ec8ab8673924e490541c7c713" -dependencies = [ - "anyhow", - "async-stream", - "async-trait", - "chrono", - "denokv_proto", - "futures", - "hex", - "log", - "num-bigint", - "rand", - "rusqlite", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tokio-stream", - "uuid", - "v8_valueserializer", -] - [[package]] name = "der" version = "0.7.9" @@ -2811,36 +1334,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", - "der_derive", "pem-rfc7468", "zeroize", ] -[[package]] -name = "der-parser" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom 7.1.3", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "der_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "deranged" version = "0.3.11" @@ -2997,56 +1494,12 @@ dependencies = [ "libloading 0.8.6", ] -[[package]] -name = "dlopen2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bc2c7ed06fd72a8513ded8d0d2f6fd2655a85d6885c48cae8625d80faf28c03" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "dlv-list" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "doctest-file" version = "1.0.0" @@ -3080,21 +1533,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" -[[package]] -name = "dprint-swc-ext" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba28c12892aadb751c2ba7001d8460faee4748a04b4edc51c7121cc67ee03db" -dependencies = [ - "num-bigint", - "rustc-hash 1.1.0", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_parser", - "text_lines", -] - [[package]] name = "drm" version = "0.12.0" @@ -3134,115 +1572,12 @@ dependencies = [ "linux-raw-sys 0.6.5", ] -[[package]] -name = "dsa" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" -dependencies = [ - "digest", - "num-bigint-dig", - "num-traits", - "pkcs8", - "rfc6979", - "sha2", - "signature", - "zeroize", -] - [[package]] name = "dyn-clone" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" -[[package]] -name = "dynasm" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" -dependencies = [ - "bitflags 1.3.2", - "byteorder", - "lazy_static", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "dynasmrt" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" -dependencies = [ - "byteorder", - "dynasm", - "memmap2 0.5.10", -] - -[[package]] -name = "ecb" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" -dependencies = [ - "cipher", -] - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core", - "serde", - "sha2", - "signature", - "subtle", - "zeroize", -] - -[[package]] -name = "ed448-goldilocks" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06924531e9e90130842b012e447f85bdaf9161bc8a0f8092be8cb70b01ebe092" -dependencies = [ - "fiat-crypto 0.1.20", - "hex", - "subtle", - "zeroize", -] - [[package]] name = "either" version = "1.13.0" @@ -3252,127 +1587,12 @@ dependencies = [ "serde", ] -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "base64ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core", - "sec1", - "serde_json", - "serdect", - "subtle", - "zeroize", -] - -[[package]] -name = "encoding" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" -dependencies = [ - "encoding-index-japanese", - "encoding-index-korean", - "encoding-index-simpchinese", - "encoding-index-singlebyte", - "encoding-index-tradchinese", -] - -[[package]] -name = "encoding-index-japanese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-korean" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-simpchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-singlebyte" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-tradchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding_index_tests" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - [[package]] name = "endi" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "enumflags2" version = "0.7.10" @@ -3394,39 +1614,12 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "env_home" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" - [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "erased-serde" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" -dependencies = [ - "serde", - "typeid", -] - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - [[package]] name = "errno" version = "0.3.10" @@ -3437,28 +1630,12 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "error-code" version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" -[[package]] -name = "error_reporter" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ae425815400e5ed474178a7a22e275a9687086a12ca63ec793ff292d8fdae8" - [[package]] name = "etagere" version = "0.2.13" @@ -3519,24 +1696,12 @@ dependencies = [ "bit_field", "half", "lebe", - "miniz_oxide 0.8.2", + "miniz_oxide", "rayon-core", "smallvec", "zune-inflate", ] -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - [[package]] name = "fast-srgb8" version = "1.0.0" @@ -3564,37 +1729,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "fastwebsockets" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26da0c7b5cef45c521a6f9cdfffdfeb6c9f5804fbac332deb5ae254634c7a6be" -dependencies = [ - "base64 0.21.7", - "bytes", - "http-body-util", - "hyper 1.5.2", - "hyper-util", - "pin-project", - "rand", - "sha1", - "simdutf8", - "thiserror 1.0.69", - "tokio", - "utf-8", -] - -[[package]] -name = "fd-lock" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys 0.52.0", -] - [[package]] name = "fdeflate" version = "0.3.7" @@ -3604,28 +1738,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - [[package]] name = "field-offset" version = "0.3.6" @@ -3633,19 +1745,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ "memoffset", - "rustc_version 0.4.1", -] - -[[package]] -name = "filetime" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", + "rustc_version", ] [[package]] @@ -3661,7 +1761,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", - "miniz_oxide 0.8.2", + "miniz_oxide", ] [[package]] @@ -3670,15 +1770,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -[[package]] -name = "float-cmp" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" -dependencies = [ - "num-traits", -] - [[package]] name = "flume" version = "0.11.1" @@ -3728,7 +1819,7 @@ checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" dependencies = [ "fontconfig-parser", "log", - "memmap2 0.9.5", + "memmap2", "slotmap", "tinyvec", "ttf-parser 0.20.0", @@ -3742,21 +1833,12 @@ checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" dependencies = [ "fontconfig-parser", "log", - "memmap2 0.9.5", + "memmap2", "slotmap", "tinyvec", "ttf-parser 0.21.1", ] -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - [[package]] name = "foreign-types" version = "0.5.0" @@ -3764,7 +1846,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared 0.3.1", + "foreign-types-shared", ] [[package]] @@ -3778,12 +1860,6 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -3799,57 +1875,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fqdn" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb540cf7bc4fe6df9d8f7f0c974cfd0dce8ed4e9e8884e73433b503ee78b4e7d" - -[[package]] -name = "freedesktop-icons" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8ef34245e0540c9a3ce7a28340b98d2c12b75da0d446da4e8224923fcaa0c16" -dependencies = [ - "dirs 5.0.1", - "once_cell", - "rust-ini 0.20.0", - "thiserror 1.0.69", - "xdg", -] - -[[package]] -name = "freedesktop_entry_parser" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db9c27b72f19a99a895f8ca89e2d26e4ef31013376e56fdafef697627306c3e4" -dependencies = [ - "nom 7.1.3", - "thiserror 1.0.69", -] - -[[package]] -name = "from_variant" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" -dependencies = [ - "proc-macro2", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "fs3" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb17cf6ed704f72485332f6ab65257460c4f9f3083934cf402bf9f5b3b600a90" -dependencies = [ - "libc", - "rustc_version 0.2.3", - "winapi", -] - [[package]] name = "fs4" version = "0.8.4" @@ -3860,31 +1885,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - -[[package]] -name = "fslock" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures" version = "0.3.31" @@ -4053,8 +2053,8 @@ name = "gauntlet-common" version = "0.0.0" dependencies = [ "anyhow", - "base64 0.22.1", - "bincode 2.0.0-rc.3", + "base64", + "bincode", "bytes", "convert_case 0.6.0", "directories", @@ -4074,6 +2074,23 @@ dependencies = [ "tonic-build", ] +[[package]] +name = "gauntlet-common-plugin-runtime" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "gauntlet-common", + "gauntlet-utils", + "gauntlet-utils-macros", + "interprocess", + "once_cell", + "regex", + "serde", + "tokio", + "tracing", +] + [[package]] name = "gauntlet-common-ui" version = "0.0.0" @@ -4120,55 +2137,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "gauntlet-plugin-runtime" -version = "0.1.0" -dependencies = [ - "anyhow", - "bincode 2.0.0-rc.3", - "bytes", - "cacao", - "cosmic-protocols", - "deno_core", - "deno_runtime", - "encoding", - "freedesktop-icons", - "freedesktop_entry_parser", - "futures", - "gauntlet-common", - "gauntlet-component-model", - "gauntlet-utils", - "gauntlet-utils-macros", - "icns", - "image 0.25.5", - "indexmap 2.7.0", - "interprocess", - "libc", - "numbat", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", - "once_cell", - "open", - "plist", - "regex", - "resvg 0.44.0", - "serde", - "smithay-client-toolkit", - "sys-locale", - "tokio", - "tokio-util", - "tracing", - "typed-path", - "uuid", - "walkdir", - "wayland-client", - "wayland-protocols-wlr 0.3.5", - "which 7.0.1", - "windows", - "x11rb", -] - [[package]] name = "gauntlet-scenario-runner" version = "0.0.0" @@ -4192,7 +2160,7 @@ dependencies = [ "futures", "gauntlet-client", "gauntlet-common", - "gauntlet-plugin-runtime", + "gauntlet-common-plugin-runtime", "gauntlet-scenario-runner", "gauntlet-utils", "git2", @@ -4309,7 +2277,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -4329,20 +2296,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", -] - -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", ] [[package]] @@ -4525,12 +2480,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - [[package]] name = "global-hotkey" version = "0.7.0" @@ -4548,18 +2497,6 @@ dependencies = [ "xkeysym", ] -[[package]] -name = "glow" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" -dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "glow" version = "0.14.2" @@ -4572,15 +2509,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "glutin_wgl_sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" -dependencies = [ - "gl_generator", -] - [[package]] name = "glutin_wgl_sys" version = "0.6.0" @@ -4664,17 +2592,6 @@ dependencies = [ "bitflags 2.6.0", ] -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - [[package]] name = "gtk" version = "0.18.2" @@ -4737,34 +2654,6 @@ dependencies = [ "svg_fmt", ] -[[package]] -name = "gzip-header" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" -dependencies = [ - "crc32fast", -] - -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.7.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "h2" version = "0.4.7" @@ -4776,7 +2665,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.2.0", + "http", "indexmap 2.7.0", "slab", "tokio", @@ -4794,16 +2683,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "halfbrown" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8588661a8607108a5ca69cab034063441a0413a0b041c13618a7dd348021ef6f" -dependencies = [ - "hashbrown 0.14.5", - "serde", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -4843,28 +2722,11 @@ dependencies = [ "hashbrown 0.14.5", ] -[[package]] -name = "hdrhistogram" -version = "7.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" -dependencies = [ - "base64 0.21.7", - "byteorder", - "crossbeam-channel", - "flate2", - "nom 7.1.3", - "num-traits", -] - [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] [[package]] name = "heck" @@ -4896,53 +2758,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" -[[package]] -name = "hickory-proto" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna", - "ipnet", - "once_cell", - "rand", - "serde", - "thiserror 1.0.69", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "hickory-resolver" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" -dependencies = [ - "cfg-if", - "futures-util", - "hickory-proto", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot 0.12.3", - "rand", - "resolv-conf", - "serde", - "smallvec", - "thiserror 1.0.69", - "tokio", - "tracing", -] - [[package]] name = "hkdf" version = "0.12.4" @@ -4970,48 +2785,12 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - -[[package]] -name = "hstr" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412" -dependencies = [ - "hashbrown 0.14.5", - "new_debug_unreachable", - "once_cell", - "phf", - "rustc-hash 1.1.0", - "triomphe", -] - [[package]] name = "htmlescape" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.2.0" @@ -5023,17 +2802,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -5041,7 +2809,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.2.0", + "http", ] [[package]] @@ -5052,8 +2820,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.2.0", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", ] @@ -5069,39 +2837,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - -[[package]] -name = "hyper" -version = "0.14.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.5.2" @@ -5111,9 +2846,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.7", - "http 1.2.0", - "http-body 1.0.1", + "h2", + "http", + "http-body", "httparse", "httpdate", "itoa", @@ -5123,30 +2858,13 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.27.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6884a48c6826ec44f524c7456b163cebe9e55a18d7b5e307cb4f100371cc767" -dependencies = [ - "futures-util", - "http 1.2.0", - "hyper 1.5.2", - "hyper-util", - "rustls 0.23.20", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - [[package]] name = "hyper-timeout" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 1.5.2", + "hyper", "hyper-util", "pin-project-lite", "tokio", @@ -5162,9 +2880,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.2.0", - "http-body 1.0.1", - "hyper 1.5.2", + "http", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", @@ -5360,7 +3078,7 @@ dependencies = [ "iced_graphics", "kurbo 0.10.4", "log", - "resvg 0.42.0", + "resvg", "rustc-hash 2.1.0", "softbuffer", "tiny-skia", @@ -5380,7 +3098,7 @@ dependencies = [ "iced_graphics", "log", "once_cell", - "resvg 0.42.0", + "resvg", "rustc-hash 2.1.0", "thiserror 1.0.69", "wgpu", @@ -5419,16 +3137,6 @@ dependencies = [ "winit", ] -[[package]] -name = "icns" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ccfbad7e08da70a5b48a924994a5afd93125ce5d45a3b0ba0b8da7bda59a40" -dependencies = [ - "byteorder", - "png 0.16.8", -] - [[package]] name = "icu_collections" version = "1.5.0" @@ -5574,12 +3282,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "image" version = "0.24.9" @@ -5593,7 +3295,7 @@ dependencies = [ "gif", "jpeg-decoder", "num-traits", - "png 0.17.16", + "png", "qoi", "tiff", ] @@ -5611,7 +3313,7 @@ dependencies = [ "gif", "image-webp", "num-traits", - "png 0.17.16", + "png", "qoi", "ravif", "rayon", @@ -5628,7 +3330,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" dependencies = [ "byteorder-lite", - "quick-error 2.0.1", + "quick-error", ] [[package]] @@ -5637,12 +3339,6 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" -[[package]] -name = "imagesize" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" - [[package]] name = "imgref" version = "1.11.0" @@ -5676,7 +3372,6 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", - "serde", ] [[package]] @@ -5690,36 +3385,6 @@ dependencies = [ "serde", ] -[[package]] -name = "inotify" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" -dependencies = [ - "bitflags 1.3.2", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding", - "generic-array", -] - [[package]] name = "instant" version = "0.1.13" @@ -5758,39 +3423,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "io-lifetimes" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" - -[[package]] -name = "ipconfig" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" -dependencies = [ - "socket2", - "widestring", - "windows-sys 0.48.0", - "winreg 0.50.0", -] - -[[package]] -name = "ipnet" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" - -[[package]] -name = "ipnetwork" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" -dependencies = [ - "serde", -] - [[package]] name = "is-docker" version = "0.2.0" @@ -5800,18 +3432,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "is-macro" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "is-wsl" version = "0.4.0" @@ -5828,15 +3448,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -5861,33 +3472,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" -[[package]] -name = "jiff" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db69f08d4fb10524cacdb074c10b296299d71274ddbc830a8ee65666867002e9" -dependencies = [ - "jiff-tzdb-platform", - "js-sys", - "wasm-bindgen", - "windows-sys 0.59.0", -] - -[[package]] -name = "jiff-tzdb" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91335e575850c5c4c673b9bd467b0e025f164ca59d0564f69d0c2ee0ffad4653" - -[[package]] -name = "jiff-tzdb-platform" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9835f0060a626fe59f160437bc725491a6af23133ea906500027d1bd2f8f4329" -dependencies = [ - "jiff-tzdb", -] - [[package]] name = "jni" version = "0.21.1" @@ -5938,30 +3522,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "junction" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be39922b087cecaba4e2d5592dedfc8bda5d4a5a1231f143337cca207950b61d" -dependencies = [ - "scopeguard", - "winapi", -] - -[[package]] -name = "k256" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", - "signature", -] - [[package]] name = "kamadak-exif" version = "0.5.5" @@ -5971,15 +3531,6 @@ dependencies = [ "mutate_once", ] -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - [[package]] name = "keyboard-types" version = "0.7.0" @@ -6008,26 +3559,6 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" -[[package]] -name = "kqueue" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - [[package]] name = "kurbo" version = "0.10.4" @@ -6069,29 +3600,6 @@ dependencies = [ "wayland-protocols-wlr 0.3.5", ] -[[package]] -name = "lazy-regex" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d8e41c97e6bc7ecb552016274b99fbb5d035e8de288c582d9b933af6677bfda" -dependencies = [ - "lazy-regex-proc_macros", - "once_cell", - "regex", -] - -[[package]] -name = "lazy-regex-proc_macros" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e1d8b05d672c53cb9c7b920bbba8783845ae4f0b076e02a3db1d02c81b4163" -dependencies = [ - "proc-macro2", - "quote", - "regex", - "syn 2.0.101", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -6143,24 +3651,6 @@ version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" -[[package]] -name = "libffi" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2" -dependencies = [ - "libc", - "libffi-sys", -] - -[[package]] -name = "libffi-sys" -version = "2.3.0" -source = "git+https://github.com/tov/libffi-rs?rev=d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b#d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b" -dependencies = [ - "cc", -] - [[package]] name = "libfuzzer-sys" version = "0.4.8" @@ -6259,12 +3749,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -6323,15 +3807,6 @@ dependencies = [ "hashbrown 0.15.2", ] -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "lz4_flex" version = "0.11.3" @@ -6371,12 +3846,6 @@ dependencies = [ "quote", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.1.0" @@ -6412,15 +3881,6 @@ dependencies = [ "digest", ] -[[package]] -name = "md4" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da5ac363534dce5fabf69949225e174fbf111a498bf0ff794c8ea1fba9f3dda" -dependencies = [ - "digest", -] - [[package]] name = "measure_time" version = "0.8.3" @@ -6437,24 +3897,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - -[[package]] -name = "memmap2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" -dependencies = [ - "libc", -] - [[package]] name = "memmap2" version = "0.9.5" @@ -6464,12 +3906,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memmem" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" - [[package]] name = "memoffset" version = "0.9.1" @@ -6479,30 +3915,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mendeleev" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f8dd6ec5207f7f69db7abb42466511394956dc85faf163de1fe393246c8b7e4" -dependencies = [ - "serde", -] - -[[package]] -name = "metal" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" -dependencies = [ - "bitflags 2.6.0", - "block", - "core-graphics-types 0.1.3", - "foreign-types 0.5.0", - "log", - "objc", - "paste", -] - [[package]] name = "metal" version = "0.29.0" @@ -6512,7 +3924,7 @@ dependencies = [ "bitflags 2.6.0", "block", "core-graphics-types 0.1.3", - "foreign-types 0.5.0", + "foreign-types", "log", "objc", "paste", @@ -6524,40 +3936,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" -dependencies = [ - "adler32", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.8.2" @@ -6568,18 +3952,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.0.3" @@ -6591,12 +3963,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "monch" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b52c1b33ff98142aecea13138bd399b68aa7ab5d9546c300988c345004001eea" - [[package]] name = "muda" version = "0.15.3" @@ -6611,7 +3977,7 @@ dependencies = [ "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", "once_cell", - "png 0.17.16", + "png", "thiserror 1.0.69", "windows-sys 0.59.0", ] @@ -6634,28 +4000,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" -[[package]] -name = "naga" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" -dependencies = [ - "arrayvec", - "bit-set 0.5.3", - "bitflags 2.6.0", - "codespan-reporting", - "hexf-parse", - "indexmap 2.7.0", - "log", - "num-traits", - "rustc-hash 1.1.0", - "serde", - "spirv", - "termcolor", - "thiserror 1.0.69", - "unicode-xid", -] - [[package]] name = "naga" version = "23.1.0" @@ -6663,7 +4007,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f" dependencies = [ "arrayvec", - "bit-set 0.8.0", + "bit-set", "bitflags 2.6.0", "cfg_aliases 0.1.1", "codespan-reporting", @@ -6677,18 +4021,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "napi_sym" -version = "0.109.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b3ee1b2d30885de3ee82429b5aebe6f22b3eae5cb290cd8d6537a62212812b" -dependencies = [ - "quote", - "serde", - "serde_json", - "syn 2.0.101", -] - [[package]] name = "ndk" version = "0.9.0" @@ -6728,42 +4060,12 @@ dependencies = [ "jni-sys", ] -[[package]] -name = "netif" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29a01b9f018d6b7b277fef6c79fdbd9bf17bb2d1e298238055cafab49baa5ee" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "new_debug_unreachable" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "libc", -] - [[package]] name = "nix" version = "0.28.0" @@ -6789,39 +4091,6 @@ dependencies = [ "memoffset", ] -[[package]] -name = "node_resolver" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e999e1cdbb49cdfa3f63ddd061c57205aa5f7be8f43bdbc4081c0f60d24d7d" -dependencies = [ - "anyhow", - "async-trait", - "boxed_error", - "deno_media_type", - "deno_package_json", - "deno_path_util", - "futures", - "lazy-regex", - "once_cell", - "path-clean", - "regex", - "serde_json", - "thiserror 1.0.69", - "tokio", - "url", -] - -[[package]] -name = "nom" -version = "5.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" -dependencies = [ - "memchr", - "version_check", -] - [[package]] name = "nom" version = "7.1.3" @@ -6838,34 +4107,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" -[[package]] -name = "notify" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" -dependencies = [ - "bitflags 2.6.0", - "crossbeam-channel", - "filetime", - "fsevent-sys", - "inotify", - "kqueue", - "libc", - "log", - "mio 0.8.11", - "walkdir", - "windows-sys 0.48.0", -] - -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -6884,8 +4125,6 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", - "rand", - "serde", ] [[package]] @@ -6901,7 +4140,6 @@ dependencies = [ "num-iter", "num-traits", "rand", - "serde", "smallvec", "zeroize", ] @@ -7014,46 +4252,6 @@ dependencies = [ "libc", ] -[[package]] -name = "numbat" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5124c7a716bd197d4ad501237fa890771f69f38b34eb87f4514fdebf0cdcaf5b" -dependencies = [ - "codespan-reporting", - "heck 0.4.1", - "indexmap 2.7.0", - "itertools 0.12.1", - "jiff", - "libc", - "mendeleev", - "num-format", - "num-integer", - "num-rational", - "num-traits", - "numbat-exchange-rates", - "plotly", - "pretty_dtoa", - "rand", - "rust-embed", - "strfmt", - "strsim", - "thiserror 1.0.69", - "unicode-ident", - "unicode-width", - "walkdir", -] - -[[package]] -name = "numbat-exchange-rates" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd1e3c3e4f9f22d0d7cdcb413f01194f6506a302a9029d95deedcd1c25df7718" -dependencies = [ - "attohttpc", - "quick-xml 0.31.0", -] - [[package]] name = "objc" version = "0.2.7" @@ -7308,15 +4506,6 @@ dependencies = [ "objc2-foundation 0.2.2", ] -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - [[package]] name = "object" version = "0.36.5" @@ -7326,15 +4515,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "oid-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" -dependencies = [ - "asn1-rs", -] - [[package]] name = "once_cell" version = "1.20.2" @@ -7347,12 +4527,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e296cf87e61c9cfc1a61c3c63a0f7f286ed4554e0e22be84e8a38e1d264a2a29" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "open" version = "5.3.1" @@ -7392,92 +4566,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "opentelemetry" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab70038c28ed37b97d8ed414b6429d343a8bbf44c9f79ec854f3a643029ba6d7" -dependencies = [ - "futures-core", - "futures-sink", - "js-sys", - "pin-project-lite", - "thiserror 1.0.69", - "tracing", -] - -[[package]] -name = "opentelemetry-http" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a8a7f5f6ba7c1b286c2fbca0454eaba116f63bbe69ed250b642d36fbb04d80" -dependencies = [ - "async-trait", - "bytes", - "http 1.2.0", - "opentelemetry", -] - -[[package]] -name = "opentelemetry-otlp" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76" -dependencies = [ - "async-trait", - "futures-core", - "http 1.2.0", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-proto", - "opentelemetry_sdk", - "prost", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tonic", - "tracing", -] - -[[package]] -name = "opentelemetry-proto" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e05acbfada5ec79023c85368af14abd0b307c015e9064d249b2a950ef459a6" -dependencies = [ - "hex", - "opentelemetry", - "opentelemetry_sdk", - "prost", - "serde", - "tonic", -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc1b6902ff63b32ef6c489e8048c5e253e2e4a803ea3ea7e783914536eb15c52" - -[[package]] -name = "opentelemetry_sdk" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8" -dependencies = [ - "async-trait", - "futures-channel", - "futures-executor", - "futures-util", - "glob", - "opentelemetry", - "percent-encoding", - "rand", - "serde_json", - "thiserror 1.0.69", - "tracing", -] - [[package]] name = "option-ext" version = "0.2.0" @@ -7493,35 +4581,16 @@ dependencies = [ "libredox", ] -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - [[package]] name = "ordered-multimap" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" dependencies = [ - "dlv-list 0.3.0", + "dlv-list", "hashbrown 0.12.3", ] -[[package]] -name = "ordered-multimap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" -dependencies = [ - "dlv-list 0.5.2", - "hashbrown 0.14.5", -] - [[package]] name = "ordered-stream" version = "0.2.0" @@ -7532,17 +4601,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "os_info" -version = "3.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ca711d8b83edbb00b44d504503cd247c9c0bd8b0fa2694f2a1a3d8165379ce" -dependencies = [ - "log", - "serde", - "windows-sys 0.52.0", -] - [[package]] name = "os_pipe" version = "1.1.5" @@ -7553,18 +4611,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "outref" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" - -[[package]] -name = "outref" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" - [[package]] name = "overload" version = "0.1.1" @@ -7589,56 +4635,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "p224" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c06436d66652bc2f01ade021592c80a2aad401570a18aa18b82e440d2b9aa1" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p521" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" -dependencies = [ - "base16ct", - "ecdsa", - "elliptic-curve", - "primeorder", - "rand_core", - "sha2", -] - [[package]] name = "palette" version = "0.7.6" @@ -7742,45 +4738,18 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "password-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "path-clean" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" - [[package]] name = "pathdiff" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -7908,21 +4877,6 @@ dependencies = [ "spki", ] -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "aes", - "cbc", - "der", - "pbkdf2", - "scrypt", - "sha2", - "spki", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -7930,8 +4884,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", - "pkcs5", - "rand_core", "spki", ] @@ -7941,61 +4893,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" -[[package]] -name = "plist" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" -dependencies = [ - "base64 0.22.1", - "indexmap 2.7.0", - "quick-xml 0.32.0", - "serde", - "time", -] - -[[package]] -name = "plotly" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e1ffd11c8a6ef0b730b9d3e46ad2404f79905825cb20223fa0547434a2dff54" -dependencies = [ - "dyn-clone", - "erased-serde", - "once_cell", - "plotly_derive", - "rand", - "rinja", - "serde", - "serde_json", - "serde_repr", - "serde_with", -] - -[[package]] -name = "plotly_derive" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e940d8d8db30c6f4cc37dab9aab61f4c9cc1e6efb6d18902ab88fa09c03560" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "png" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "deflate", - "miniz_oxide 0.3.7", -] - [[package]] name = "png" version = "0.17.16" @@ -8006,7 +4903,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.8.2", + "miniz_oxide", ] [[package]] @@ -8024,18 +4921,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -8057,15 +4942,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" -[[package]] -name = "pretty_dtoa" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a239bcdfda2c685fda1add3b4695c06225f50075e3cfb5b954e91545587edff2" -dependencies = [ - "ryu_floating_decimal", -] - [[package]] name = "prettyplease" version = "0.2.25" @@ -8076,15 +4952,6 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -8137,29 +5004,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "proc-macro-rules" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" -dependencies = [ - "proc-macro-rules-macros", - "proc-macro2", - "syn 2.0.101", -] - -[[package]] -name = "proc-macro-rules-macros" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207fffb0fe655d1d47f6af98cc2793405e85929bdbc420d685554ff07be27ac7" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "proc-macro-utils" version = "0.10.0" @@ -8251,35 +5095,6 @@ dependencies = [ "prost", ] -[[package]] -name = "psm" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" -dependencies = [ - "cc", -] - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "qoi" version = "0.4.1" @@ -8289,36 +5104,12 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quick-error" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" -[[package]] -name = "quick-xml" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" -dependencies = [ - "memchr", -] - -[[package]] -name = "quick-xml" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" -dependencies = [ - "memchr", -] - [[package]] name = "quick-xml" version = "0.36.2" @@ -8337,22 +5128,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - [[package]] name = "rand" version = "0.8.5" @@ -8449,7 +5224,7 @@ dependencies = [ "avif-serialize", "imgref", "loop9", - "quick-error 2.0.1", + "quick-error", "rav1e", "rayon", "rgb", @@ -8535,26 +5310,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "ref-cast" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "regex" version = "1.11.1" @@ -8605,16 +5360,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" -[[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error 1.2.3", -] - [[package]] name = "resvg" version = "0.42.0" @@ -8628,31 +5373,7 @@ dependencies = [ "rgb", "svgtypes", "tiny-skia", - "usvg 0.42.0", -] - -[[package]] -name = "resvg" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958" -dependencies = [ - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg 0.44.0", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", + "usvg", ] [[package]] @@ -8679,70 +5400,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rinja" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5" -dependencies = [ - "humansize", - "itoa", - "percent-encoding", - "rinja_derive", - "serde", - "serde_json", -] - -[[package]] -name = "rinja_derive" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b" -dependencies = [ - "basic-toml", - "memchr", - "mime", - "mime_guess", - "proc-macro2", - "quote", - "rinja_parser", - "rustc-hash 2.1.0", - "serde", - "syn 2.0.101", -] - -[[package]] -name = "rinja_parser" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610" -dependencies = [ - "memchr", - "nom 7.1.3", - "serde", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest", -] - -[[package]] -name = "ron" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" -dependencies = [ - "base64 0.21.7", - "bitflags 2.6.0", - "serde", - "serde_derive", -] - [[package]] name = "roxmltree" version = "0.20.0" @@ -8769,55 +5426,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rusqlite" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" -dependencies = [ - "bitflags 2.6.0", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", -] - -[[package]] -name = "rust-embed" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "shellexpand", - "syn 2.0.101", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" -dependencies = [ - "sha2", - "walkdir", -] - [[package]] name = "rust-ini" version = "0.18.0" @@ -8825,17 +5433,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" dependencies = [ "cfg-if", - "ordered-multimap 0.4.3", -] - -[[package]] -name = "rust-ini" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" -dependencies = [ - "cfg-if", - "ordered-multimap 0.7.3", + "ordered-multimap", ] [[package]] @@ -8866,31 +5464,13 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.24", -] - -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom 7.1.3", + "semver", ] [[package]] @@ -8900,26 +5480,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", - "errno 0.3.10", + "errno", "libc", "linux-raw-sys 0.4.14", "windows-sys 0.59.0", ] -[[package]] -name = "rustls" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" -dependencies = [ - "log", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - [[package]] name = "rustls" version = "0.23.20" @@ -8935,46 +5501,12 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" -[[package]] -name = "rustls-tokio-stream" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22557157d7395bc30727745b365d923f1ecc230c4c80b176545f3f4f08c46e33" -dependencies = [ - "futures", - "rustls 0.23.20", - "socket2", - "tokio", -] - [[package]] name = "rustls-webpki" version = "0.102.8" @@ -9009,65 +5541,12 @@ dependencies = [ "unicode-script", ] -[[package]] -name = "rustyline" -version = "13.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "clipboard-win", - "fd-lock", - "home", - "libc", - "log", - "memchr", - "nix 0.27.1", - "radix_trie", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "winapi", -] - [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "ryu-js" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" - -[[package]] -name = "ryu_floating_decimal" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "700de91d5fd6091442d00fdd9ee790af6d4f0f480562b0f5a1e8f59e90aafe73" - -[[package]] -name = "saffron" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03fb9a628596fc7590eb7edbf7b0613287be78df107f5f97b118aad59fb2eea9" -dependencies = [ - "chrono", - "nom 5.1.3", -] - -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - [[package]] name = "same-file" version = "1.0.6" @@ -9077,15 +5556,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "schemars" version = "0.8.22" @@ -9122,18 +5592,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "password-hash", - "pbkdf2", - "salsa20", - "sha2", -] - [[package]] name = "sctk-adwaita" version = "0.10.1" @@ -9142,64 +5600,17 @@ checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" dependencies = [ "ab_glyph", "log", - "memmap2 0.9.5", + "memmap2", "smithay-client-toolkit", "tiny-skia", ] -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "serdect", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.6.0", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "self_cell" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.24" @@ -9209,12 +5620,6 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" version = "1.0.216" @@ -9224,25 +5629,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" -dependencies = [ - "serde", -] - [[package]] name = "serde_derive" version = "1.0.216" @@ -9271,7 +5657,6 @@ version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ - "indexmap 2.7.0", "itoa", "memchr", "ryu", @@ -9310,59 +5695,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_v8" -version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a783242d2af51d6955cc04bf2b64adb643ab588b61e9573c908a69dabf8c2f" -dependencies = [ - "num-bigint", - "serde", - "smallvec", - "thiserror 1.0.69", - "v8", -] - -[[package]] -name = "serde_with" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.7.0", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "serdect" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" -dependencies = [ - "base16ct", - "serde", -] - [[package]] name = "sha1" version = "0.10.6" @@ -9385,16 +5717,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -9404,31 +5726,12 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shellexpand" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" -dependencies = [ - "dirs 5.0.1", -] - [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -9448,36 +5751,12 @@ dependencies = [ "rand_core", ] -[[package]] -name = "simd-abstraction" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" -dependencies = [ - "outref 0.1.0", -] - [[package]] name = "simd-adler32" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "simd-json" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2bcf6c6e164e81bc7a5d49fc6988b3d515d9e8c07457d7b74ffb9324b9cd40" -dependencies = [ - "getrandom", - "halfbrown", - "ref-cast", - "serde", - "serde_json", - "simdutf8", - "value-trait", -] - [[package]] name = "simd_helpers" version = "0.1.0" @@ -9487,12 +5766,6 @@ dependencies = [ "quote", ] -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - [[package]] name = "simplecss" version = "0.2.1" @@ -9551,15 +5824,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "sm3" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebb9a3b702d0a7e33bc4d85a14456633d2b165c2ad839c5fd9a8417c1ab15860" -dependencies = [ - "digest", -] - [[package]] name = "smallvec" version = "1.13.2" @@ -9569,17 +5833,6 @@ dependencies = [ "serde", ] -[[package]] -name = "smartstring" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" -dependencies = [ - "autocfg", - "static_assertions", - "version_check", -] - [[package]] name = "smithay-client-toolkit" version = "0.19.2" @@ -9587,14 +5840,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ "bitflags 2.6.0", - "bytemuck", "calloop 0.13.0", "calloop-wayland-source 0.3.0", "cursor-icon", "libc", "log", - "memmap2 0.9.5", - "pkg-config", + "memmap2", "rustix", "thiserror 1.0.69", "wayland-backend", @@ -9604,7 +5855,6 @@ dependencies = [ "wayland-protocols 0.32.5", "wayland-protocols-wlr 0.3.5", "wayland-scanner", - "xkbcommon", "xkeysym", ] @@ -9650,10 +5900,10 @@ dependencies = [ "core-graphics 0.24.0", "drm", "fastrand", - "foreign-types 0.5.0", + "foreign-types", "js-sys", "log", - "memmap2 0.9.5", + "memmap2", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-quartz-core", @@ -9670,44 +5920,6 @@ dependencies = [ "x11rb", ] -[[package]] -name = "sourcemap" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" -dependencies = [ - "base64-simd 0.7.0", - "bitvec", - "data-encoding", - "debugid", - "if_chain", - "rustc-hash 1.1.0", - "rustc_version 0.2.3", - "serde", - "serde_json", - "unicode-id-start", - "url", -] - -[[package]] -name = "sourcemap" -version = "9.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c4ea7042fd1a155ad95335b5d505ab00d5124ea0332a06c8390d200bb1a76a" -dependencies = [ - "base64-simd 0.7.0", - "bitvec", - "data-encoding", - "debugid", - "if_chain", - "rustc-hash 1.1.0", - "rustc_version 0.2.3", - "serde", - "serde_json", - "unicode-id-start", - "url", -] - [[package]] name = "spin" version = "0.9.8" @@ -9742,7 +5954,7 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" dependencies = [ - "nom 7.1.3", + "nom", "unicode_categories", ] @@ -9844,7 +6056,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" dependencies = [ "atoi", - "base64 0.22.1", + "base64", "bitflags 2.6.0", "byteorder", "bytes", @@ -9886,7 +6098,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" dependencies = [ "atoi", - "base64 0.22.1", + "base64", "bitflags 2.6.0", "byteorder", "crc", @@ -9946,58 +6158,21 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "stacker" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.59.0", -] - [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strfmt" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8348af2d9fc3258c8733b8d9d8db2e56f54b2363a4b5b81585c7875ed65e65" - [[package]] name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" dependencies = [ - "float-cmp 0.9.0", + "float-cmp", ] -[[package]] -name = "string_enum" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "stringcase" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04028eeb851ed08af6aba5caa29f2d59a13ed168cee4d6bd753aeefcf1d636b0" - [[package]] name = "stringprep" version = "0.1.5" @@ -10015,28 +6190,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.101", -] - [[package]] name = "subtle" version = "2.6.1" @@ -10070,373 +6223,6 @@ dependencies = [ "zeno", ] -[[package]] -name = "swc_allocator" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76aa0eb65c0f39f9b6d82a7e5192c30f7ac9a78f084a21f270de1d8c600ca388" -dependencies = [ - "bumpalo", - "hashbrown 0.14.5", - "ptr_meta", - "rustc-hash 1.1.0", - "triomphe", -] - -[[package]] -name = "swc_atoms" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6567e4e67485b3e7662b486f1565bdae54bd5b9d6b16b2ba1a9babb1e42125" -dependencies = [ - "hstr", - "once_cell", - "rustc-hash 1.1.0", - "serde", -] - -[[package]] -name = "swc_cached" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c" -dependencies = [ - "ahash 0.8.11", - "anyhow", - "dashmap", - "once_cell", - "regex", - "serde", -] - -[[package]] -name = "swc_common" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12d0a8eaaf1606c9207077d75828008cb2dfb51b095a766bd2b72ef893576e31" -dependencies = [ - "ast_node", - "better_scoped_tls", - "cfg-if", - "either", - "from_variant", - "new_debug_unreachable", - "num-bigint", - "once_cell", - "rustc-hash 1.1.0", - "serde", - "siphasher 0.3.11", - "sourcemap 9.1.2", - "swc_allocator", - "swc_atoms", - "swc_eq_ignore_macros", - "swc_visit", - "tracing", - "unicode-width", - "url", -] - -[[package]] -name = "swc_config" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" -dependencies = [ - "anyhow", - "indexmap 2.7.0", - "serde", - "serde_json", - "swc_cached", - "swc_config_macro", -] - -[[package]] -name = "swc_config_macro" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5f56139042c1a95b54f5ca48baa0e0172d369bcc9d3d473dad1de36bae8399" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "swc_ecma_ast" -version = "0.118.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f866d12e4d519052b92a0a86d1ac7ff17570da1272ca0c89b3d6f802cd79df" -dependencies = [ - "bitflags 2.6.0", - "is-macro", - "num-bigint", - "phf", - "scoped-tls", - "serde", - "string_enum", - "swc_atoms", - "swc_common", - "unicode-id-start", -] - -[[package]] -name = "swc_ecma_codegen" -version = "0.155.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7641608ef117cfbef9581a99d02059b522fcca75e5244fa0cbbd8606689c6f" -dependencies = [ - "memchr", - "num-bigint", - "once_cell", - "serde", - "sourcemap 9.1.2", - "swc_allocator", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_codegen_macros", - "tracing", -] - -[[package]] -name = "swc_ecma_codegen_macros" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859fabde36db38634f3fad548dd5e3410c1aebba1b67a3c63e67018fa57a0bca" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "swc_ecma_loader" -version = "0.49.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55fa3d55045b97894bfb04d38aff6d6302ac8a6a38e3bb3dfb0d20475c4974a9" -dependencies = [ - "anyhow", - "pathdiff", - "serde", - "swc_atoms", - "swc_common", - "tracing", -] - -[[package]] -name = "swc_ecma_parser" -version = "0.149.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683dada14722714588b56481399c699378b35b2ba4deb5c4db2fb627a97fb54b" -dependencies = [ - "either", - "new_debug_unreachable", - "num-bigint", - "num-traits", - "phf", - "serde", - "smallvec", - "smartstring", - "stacker", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "tracing", - "typed-arena", -] - -[[package]] -name = "swc_ecma_transforms_base" -version = "0.145.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f21494e75d0bd8ef42010b47cabab9caaed8f2207570e809f6f4eb51a710d1" -dependencies = [ - "better_scoped_tls", - "bitflags 2.6.0", - "indexmap 2.7.0", - "once_cell", - "phf", - "rustc-hash 1.1.0", - "serde", - "smallvec", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_parser", - "swc_ecma_utils", - "swc_ecma_visit", - "tracing", -] - -[[package]] -name = "swc_ecma_transforms_classes" -version = "0.134.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3d884594385bea9405a2e1721151470d9a14d3ceec5dd773c0ca6894791601" -dependencies = [ - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms_macros" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500a1dadad1e0e41e417d633b3d6d5de677c9e0d3159b94ba3348436cdb15aab" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "swc_ecma_transforms_proposal" -version = "0.179.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79938ff510fc647febd8c6c3ef4143d099fdad87a223680e632623d056dae2dd" -dependencies = [ - "either", - "rustc-hash 1.1.0", - "serde", - "smallvec", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_transforms_classes", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms_react" -version = "0.191.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" -dependencies = [ - "base64 0.21.7", - "dashmap", - "indexmap 2.7.0", - "once_cell", - "serde", - "sha1", - "string_enum", - "swc_allocator", - "swc_atoms", - "swc_common", - "swc_config", - "swc_ecma_ast", - "swc_ecma_parser", - "swc_ecma_transforms_base", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms_typescript" -version = "0.198.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15455da4768f97186c40523e83600495210c11825d3a44db43383fd81eace88d" -dependencies = [ - "ryu-js", - "serde", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_transforms_react", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_utils" -version = "0.134.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029eec7dd485923a75b5a45befd04510288870250270292fc2c1b3a9e7547408" -dependencies = [ - "indexmap 2.7.0", - "num_cpus", - "once_cell", - "rustc-hash 1.1.0", - "ryu-js", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_visit", - "tracing", - "unicode-id", -] - -[[package]] -name = "swc_ecma_visit" -version = "0.104.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1c6802e68e51f336e8bc9644e9ff9da75d7da9c1a6247d532f2e908aa33e81" -dependencies = [ - "new_debug_unreachable", - "num-bigint", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_visit", - "tracing", -] - -[[package]] -name = "swc_eq_ignore_macros" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "swc_macros_common" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "swc_visit" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceb044142ba2719ef9eb3b6b454fce61ab849eb696c34d190f04651955c613d" -dependencies = [ - "either", - "new_debug_unreachable", -] - -[[package]] -name = "swc_visit_macros" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92807d840959f39c60ce8a774a3f83e8193c658068e6d270dbe0a05e40e90b41" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - [[package]] name = "syn" version = "1.0.109" @@ -10444,7 +6230,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote", "unicode-ident", ] @@ -10465,18 +6250,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - [[package]] name = "synstructure" version = "0.13.1" @@ -10518,7 +6291,7 @@ checksum = "f8d0582f186c0a6d55655d24543f15e43607299425c5ad8352c242b914b31856" dependencies = [ "aho-corasick", "arc-swap", - "base64 0.22.1", + "base64", "bitpacking", "byteorder", "census", @@ -10535,7 +6308,7 @@ dependencies = [ "lru", "lz4_flex", "measure_time", - "memmap2 0.9.5", + "memmap2", "num_cpus", "once_cell", "oneshot", @@ -10616,7 +6389,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "847434d4af57b32e309f4ab1b4f1707a6c566656264caa427ff4285c4d9d0b82" dependencies = [ - "nom 7.1.3", + "nom", ] [[package]] @@ -10651,12 +6424,6 @@ dependencies = [ "serde", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "target-lexicon" version = "0.12.16" @@ -10685,15 +6452,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "text_lines" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd5828de7deaa782e1dd713006ae96b3bee32d3279b79eb67ecf8072c059bcf" -dependencies = [ - "serde", -] - [[package]] name = "thiserror" version = "1.0.69" @@ -10788,15 +6546,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tiny-skia" version = "0.11.4" @@ -10808,7 +6557,7 @@ dependencies = [ "bytemuck", "cfg-if", "log", - "png 0.17.16", + "png", "tiny-skia-path", ] @@ -10870,25 +6619,13 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.3", - "parking_lot 0.12.3", + "mio", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", ] -[[package]] -name = "tokio-eld" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9166030f05d6bc5642bdb8f8c2be31eb3c02cd465d662bcdc2df82d4aa41a584" -dependencies = [ - "hdrhistogram", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.4.0" @@ -10900,40 +6637,6 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "tokio-metrics" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eace09241d62c98b7eeb1107d4c5c64ca3bd7da92e8c218c153ab3a78f9be112" -dependencies = [ - "futures-util", - "pin-project-lite", - "tokio", - "tokio-stream", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" -dependencies = [ - "rustls 0.23.20", - "tokio", -] - -[[package]] -name = "tokio-socks" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" -dependencies = [ - "either", - "futures-util", - "thiserror 1.0.69", - "tokio", -] - [[package]] name = "tokio-stream" version = "0.1.17" @@ -10953,12 +6656,8 @@ checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", - "futures-io", "futures-sink", - "futures-util", - "hashbrown 0.14.5", "pin-project-lite", - "slab", "tokio", ] @@ -11027,13 +6726,13 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.22.1", + "base64", "bytes", - "h2 0.4.7", - "http 1.2.0", - "http-body 1.0.1", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.5.2", + "hyper", "hyper-timeout", "hyper-util", "percent-encoding", @@ -11096,26 +6795,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "tower-http" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" -dependencies = [ - "async-compression", - "bitflags 2.6.0", - "bytes", - "futures-core", - "http 1.2.0", - "http-body 1.0.1", - "http-body-util", - "pin-project-lite", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-layer" version = "0.3.3" @@ -11205,7 +6884,7 @@ dependencies = [ "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", "once_cell", - "png 0.17.16", + "png", "thiserror 1.0.69", "windows-sys 0.59.0", ] @@ -11218,21 +6897,11 @@ checksum = "aac5e8971f245c3389a5a76e648bfc80803ae066a1243a75db0064d7c1129d63" dependencies = [ "fnv", "memchr", - "nom 7.1.3", + "nom", "once_cell", "petgraph", ] -[[package]] -name = "triomphe" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" -dependencies = [ - "serde", - "stable_deref_trait", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -11257,35 +6926,12 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "rand", - "static_assertions", -] - -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - [[package]] name = "typed-path" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41713888c5ccfd99979fcd1afd47b71652e331b3d4a0e19d30769e80fec76cce" -[[package]] -name = "typeid" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" - [[package]] name = "typenum" version = "1.17.0" @@ -11303,53 +6949,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-ucd-ident" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - -[[package]] -name = "unicase" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" - [[package]] name = "unicode-bidi" version = "0.3.18" @@ -11368,18 +6967,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" -[[package]] -name = "unicode-id" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" - -[[package]] -name = "unicode-id-start" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" - [[package]] name = "unicode-ident" version = "1.0.14" @@ -11443,16 +7030,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - [[package]] name = "untrusted" version = "0.9.0" @@ -11465,11 +7042,11 @@ version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" dependencies = [ - "base64 0.22.1", + "base64", "flate2", "log", "once_cell", - "rustls 0.23.20", + "rustls", "rustls-pki-types", "url", "webpki-roots", @@ -11484,19 +7061,6 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", -] - -[[package]] -name = "urlpattern" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" -dependencies = [ - "regex", - "serde", - "unic-ucd-ident", - "url", ] [[package]] @@ -11505,11 +7069,11 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" dependencies = [ - "base64 0.22.1", + "base64", "data-url", "flate2", "fontdb 0.18.0", - "imagesize 0.12.0", + "imagesize", "kurbo 0.11.1", "log", "pico-args", @@ -11526,34 +7090,6 @@ dependencies = [ "xmlwriter", ] -[[package]] -name = "usvg" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447e703d7223b067607655e625e0dbca80822880248937da65966194c4864e6" -dependencies = [ - "base64 0.22.1", - "data-url", - "flate2", - "imagesize 0.13.0", - "kurbo 0.11.1", - "log", - "pico-args", - "roxmltree", - "simplecss", - "siphasher 1.0.1", - "strict-num", - "svgtypes", - "tiny-skia-path", - "xmlwriter", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf16_iter" version = "1.0.5" @@ -11588,38 +7124,6 @@ dependencies = [ "serde", ] -[[package]] -name = "v8" -version = "130.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee0be58935708fa4d7efb970c6cf9f2d9511d24ee24246481a65b6ee167348d" -dependencies = [ - "bindgen", - "bitflags 2.6.0", - "fslock", - "gzip-header", - "home", - "miniz_oxide 0.7.4", - "once_cell", - "paste", - "which 6.0.3", -] - -[[package]] -name = "v8_valueserializer" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97599c400fc79925922b58303e98fcb8fa88f573379a08ddb652e72cbd2e70f6" -dependencies = [ - "bitflags 2.6.0", - "encoding_rs", - "indexmap 2.7.0", - "num-bigint", - "serde", - "thiserror 1.0.69", - "wtf8", -] - [[package]] name = "v_frame" version = "0.3.8" @@ -11637,18 +7141,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "value-trait" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9170e001f458781e92711d2ad666110f153e4e50bfd5cbd02db6547625714187" -dependencies = [ - "float-cmp 0.10.0", - "halfbrown", - "itoa", - "ryu", -] - [[package]] name = "vcpkg" version = "0.2.15" @@ -11725,12 +7217,6 @@ version = "0.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314" -[[package]] -name = "vsimd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" - [[package]] name = "walkdir" version = "2.5.0" @@ -11844,15 +7330,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasm_dep_analyzer" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f270206a91783fd90625c8bb0d8fbd459d0b1d1bf209b656f713f01ae7c04b8" -dependencies = [ - "thiserror 1.0.69", -] - [[package]] name = "waycrate_xkbkeycode" version = "0.13.99" @@ -11860,7 +7337,7 @@ source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch= dependencies = [ "bitflags 2.6.0", "calloop 0.14.2", - "memmap2 0.9.5", + "memmap2", "smol_str", "tracing", "wayland-backend", @@ -11938,7 +7415,6 @@ dependencies = [ "wayland-backend", "wayland-client", "wayland-scanner", - "wayland-server", ] [[package]] @@ -11991,7 +7467,6 @@ dependencies = [ "wayland-client", "wayland-protocols 0.32.5", "wayland-scanner", - "wayland-server", ] [[package]] @@ -12001,24 +7476,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" dependencies = [ "proc-macro2", - "quick-xml 0.36.2", + "quick-xml", "quote", ] -[[package]] -name = "wayland-server" -version = "0.31.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89532cc712a2adb119eb4d09694b402576052254d0bb284f82ac1c47fb786ad" -dependencies = [ - "bitflags 2.6.0", - "downcast-rs", - "io-lifetimes", - "rustix", - "wayland-backend", - "wayland-scanner", -] - [[package]] name = "wayland-sys" version = "0.31.5" @@ -12051,15 +7512,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-root-certs" -version = "0.26.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd5da49bdf1f30054cfe0b8ce2958b8fbeb67c4d82c8967a598af481bef255c" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "webpki-roots" version = "0.26.7" @@ -12086,7 +7538,7 @@ dependencies = [ "document-features", "js-sys", "log", - "naga 23.1.0", + "naga", "parking_lot 0.12.3", "profiling", "raw-window-handle", @@ -12095,38 +7547,9 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "wgpu-core 23.0.1", - "wgpu-hal 23.0.1", - "wgpu-types 23.0.0", -] - -[[package]] -name = "wgpu-core" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39" -dependencies = [ - "arrayvec", - "bit-vec 0.6.3", - "bitflags 2.6.0", - "cfg_aliases 0.1.1", - "codespan-reporting", - "document-features", - "indexmap 2.7.0", - "log", - "naga 0.20.0", - "once_cell", - "parking_lot 0.12.3", - "profiling", - "raw-window-handle", - "ron", - "rustc-hash 1.1.0", - "serde", - "smallvec", - "thiserror 1.0.69", - "web-sys", - "wgpu-hal 0.21.1", - "wgpu-types 0.20.0", + "wgpu-core", + "wgpu-hal", + "wgpu-types", ] [[package]] @@ -12136,13 +7559,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a" dependencies = [ "arrayvec", - "bit-vec 0.8.0", + "bit-vec", "bitflags 2.6.0", "cfg_aliases 0.1.1", "document-features", "indexmap 2.7.0", "log", - "naga 23.1.0", + "naga", "once_cell", "parking_lot 0.12.3", "profiling", @@ -12150,50 +7573,8 @@ dependencies = [ "rustc-hash 1.1.0", "smallvec", "thiserror 1.0.69", - "wgpu-hal 23.0.1", - "wgpu-types 23.0.0", -] - -[[package]] -name = "wgpu-hal" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222" -dependencies = [ - "android_system_properties", - "arrayvec", - "ash 0.37.3+1.3.251", - "bit-set 0.5.3", - "bitflags 2.6.0", - "block", - "cfg_aliases 0.1.1", - "core-graphics-types 0.1.3", - "d3d12", - "glow 0.13.1", - "glutin_wgl_sys 0.5.0", - "gpu-alloc", - "gpu-descriptor", - "js-sys", - "khronos-egl", - "libc", - "libloading 0.8.6", - "log", - "metal 0.28.0", - "naga 0.20.0", - "ndk-sys 0.5.0+25.2.9519653", - "objc", - "once_cell", - "parking_lot 0.12.3", - "profiling", - "range-alloc", - "raw-window-handle", - "rustc-hash 1.1.0", - "smallvec", - "thiserror 1.0.69", - "wasm-bindgen", - "web-sys", - "wgpu-types 0.20.0", - "winapi", + "wgpu-hal", + "wgpu-types", ] [[package]] @@ -12204,15 +7585,15 @@ checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821" dependencies = [ "android_system_properties", "arrayvec", - "ash 0.38.0+1.3.281", - "bit-set 0.8.0", + "ash", + "bit-set", "bitflags 2.6.0", "block", "bytemuck", "cfg_aliases 0.1.1", "core-graphics-types 0.1.3", - "glow 0.14.2", - "glutin_wgl_sys 0.6.0", + "glow", + "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", "gpu-descriptor", @@ -12221,8 +7602,8 @@ dependencies = [ "libc", "libloading 0.8.6", "log", - "metal 0.29.0", - "naga 23.1.0", + "metal", + "naga", "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", @@ -12236,23 +7617,11 @@ dependencies = [ "thiserror 1.0.69", "wasm-bindgen", "web-sys", - "wgpu-types 23.0.0", + "wgpu-types", "windows", "windows-core 0.58.0", ] -[[package]] -name = "wgpu-types" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" -dependencies = [ - "bitflags 2.6.0", - "js-sys", - "serde", - "web-sys", -] - [[package]] name = "wgpu-types" version = "23.0.0" @@ -12264,42 +7633,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "which" -version = "6.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" -dependencies = [ - "either", - "home", - "rustix", - "winsafe", -] - -[[package]] -name = "which" -version = "7.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a9e33648339dc1642b0e36e21b3385e6148e289226f657c809dee59df5028" -dependencies = [ - "either", - "env_home", - "rustix", - "winsafe", -] - [[package]] name = "whoami" version = "1.5.2" @@ -12308,7 +7641,6 @@ checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ "redox_syscall 0.5.8", "wasite", - "web-sys", ] [[package]] @@ -12669,7 +8001,7 @@ dependencies = [ "dpi", "js-sys", "libc", - "memmap2 0.9.5", + "memmap2", "ndk", "objc2 0.5.2", "objc2-app-kit 0.2.2", @@ -12727,22 +8059,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "winsafe" -version = "0.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" - [[package]] name = "wl-clipboard-rs" version = "0.8.1" @@ -12775,21 +8091,6 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" -[[package]] -name = "wtf8" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c01ae8492c38f52376efd3a17d0994b6bcf3df1e39c0226d458b7d81670b2a06" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "x11-dl" version = "2.21.0" @@ -12822,47 +8123,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" -[[package]] -name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" -dependencies = [ - "curve25519-dalek", - "rand_core", - "serde", - "zeroize", -] - -[[package]] -name = "x509-parser" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom 7.1.3", - "oid-registry", - "rusticata-macros", - "thiserror 1.0.69", - "time", -] - [[package]] name = "xcursor" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" -[[package]] -name = "xdg" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" - [[package]] name = "xdg-home" version = "1.3.0" @@ -12873,17 +8139,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "xkbcommon" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" -dependencies = [ - "libc", - "memmap2 0.8.0", - "xkeysym", -] - [[package]] name = "xkbcommon-dl" version = "0.4.2" @@ -12902,9 +8157,6 @@ name = "xkeysym" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" -dependencies = [ - "bytemuck", -] [[package]] name = "xml-rs" @@ -12945,7 +8197,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.101", - "synstructure 0.13.1", + "synstructure", ] [[package]] @@ -13055,7 +8307,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.101", - "synstructure 0.13.1", + "synstructure", ] [[package]] @@ -13063,20 +8315,6 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] [[package]] name = "zerovec" diff --git a/Cargo.toml b/Cargo.toml index fdd289e..2af9ca7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,14 +10,15 @@ members = [ "rust/server", "rust/common", "rust/common_ui", + "rust/common_plugin_runtime", "rust/utils", "rust/utils_macros", "rust/cli", "rust/component_model", "rust/scenario_runner", - "rust/plugin_runtime", "rust/manifest_schema", ] + [workspace.package] edition = "2021" @@ -37,12 +38,12 @@ iced_layershell = { git = "https://github.com/project-gauntlet/exwlshelleventlo # workspaces gauntlet-common = { path = "./rust/common" } gauntlet-common-ui = { path = "./rust/common_ui" } +gauntlet-common-plugin-runtime = { path = "./rust/common_plugin_runtime" } gauntlet-management-client = { path = "./rust/management_client" } gauntlet-client = { path = "./rust/client" } gauntlet-server = { path = "./rust/server" } gauntlet-utils = { path = "./rust/utils" } gauntlet-utils-macros = { path = "./rust/utils_macros" } -gauntlet-plugin-runtime = { path = "./rust/plugin_runtime" } gauntlet-component-model = { path = "./rust/component_model" } gauntlet-scenario-runner = { path = "./rust/scenario_runner" } @@ -84,6 +85,3 @@ opt-level = "s" lto = "thin" strip = true -[patch.crates-io] -# NOTE https://github.com/ipetkov/crane/issues/336 -libffi-sys = { git = "https://github.com/tov/libffi-rs", rev = "d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b" } diff --git a/js/build/src/main.ts b/js/build/src/main.ts index 87c0a32..40e2a0b 100644 --- a/js/build/src/main.ts +++ b/js/build/src/main.ts @@ -205,7 +205,13 @@ function build(projectRoot: string, arch: string, profile: string) { } function buildRust(projectRoot: string, arch: string, profile: string) { - console.log("Building rust...") + console.log("Building rust plugin_runtime...") + const pluginRuntimeRoot = path.resolve(projectRoot, 'rust', 'plugin_runtime'); + spawnWithErrors('cargo', ['build', '--profile', profile, '--features', 'release', '--target', arch], { + cwd: pluginRuntimeRoot + }); + + console.log("Building rust core...") spawnWithErrors('cargo', ['build', '--profile', profile, '--features', 'release', '--target', arch], { cwd: projectRoot }); @@ -597,4 +603,4 @@ function spawnWithErrors(command: string, args: string[], options: SpawnSyncOpti if (npmRunResult.status !== 0) { throw new Error(`Unable to run ${command} ${args}, status: ${JSON.stringify(npmRunResult, null, 2)}`); } -} \ No newline at end of file +} diff --git a/js/scenario_runner_cli/src/main.ts b/js/scenario_runner_cli/src/main.ts index 3d51a78..2390f56 100644 --- a/js/scenario_runner_cli/src/main.ts +++ b/js/scenario_runner_cli/src/main.ts @@ -35,7 +35,11 @@ async function runScenarios(expectedPlugin: string | undefined) { const scenariosData = path.join(scenarios, "scenarios"); const scenariosRun = path.join(scenarios, "run"); - console.log("Building server") + console.log("Building plugin_runtime") + const pluginRuntimeRoot = path.resolve(projectRoot, 'rust', 'plugin_runtime'); + spawnSync('cargo', ['build', '--features', 'release'], { + cwd: pluginRuntimeRoot + }); console.log("Building scenario plugins") buildScenarioPlugins(projectRoot) diff --git a/rust/common_plugin_runtime/Cargo.toml b/rust/common_plugin_runtime/Cargo.toml new file mode 100644 index 0000000..9bc3949 --- /dev/null +++ b/rust/common_plugin_runtime/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "gauntlet-common-plugin-runtime" +version = "0.1.0" +edition.workspace = true + +[dependencies] +# workspaces +gauntlet-utils.workspace = true +gauntlet-utils-macros.workspace = true +gauntlet-common.workspace = true + +# shared +anyhow.workspace = true +bincode.workspace = true +serde.workspace = true +tracing.workspace = true +interprocess.workspace = true +tokio.workspace = true +once_cell.workspace = true +regex.workspace = true diff --git a/rust/plugin_runtime/src/api.rs b/rust/common_plugin_runtime/src/api.rs similarity index 98% rename from rust/plugin_runtime/src/api.rs rename to rust/common_plugin_runtime/src/api.rs index 5f06604..5345450 100644 --- a/rust/plugin_runtime/src/api.rs +++ b/rust/common_plugin_runtime/src/api.rs @@ -8,7 +8,7 @@ use gauntlet_utils_macros::boundary_gen; use crate::model::JsClipboardData; use crate::model::JsGeneratedSearchItem; use crate::model::JsPreferenceUserData; -use crate::JsUiRenderLocation; +use crate::model::JsUiRenderLocation; #[allow(async_fn_in_trait)] #[boundary_gen(bincode, in_process)] diff --git a/rust/common_plugin_runtime/src/lib.rs b/rust/common_plugin_runtime/src/lib.rs new file mode 100644 index 0000000..ce7771f --- /dev/null +++ b/rust/common_plugin_runtime/src/lib.rs @@ -0,0 +1,100 @@ +use std::fmt::Debug; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering; + +use anyhow::Context; +use bincode::Decode; +use bincode::Encode; +use gauntlet_utils::channel::Payload; +use gauntlet_utils::channel::RequestReceiver; +use interprocess::local_socket::tokio::prelude::*; +use interprocess::local_socket::tokio::RecvHalf; +use interprocess::local_socket::tokio::SendHalf; +use interprocess::local_socket::tokio::Stream; +use interprocess::local_socket::GenericFilePath; +use interprocess::local_socket::NameType; +use interprocess::local_socket::ToNsName; +use once_cell::sync::Lazy; +use regex::Regex; +use serde::de::DeserializeOwned; +use serde::Deserialize; +use serde::Serialize; +use tokio::io::AsyncBufReadExt; +use tokio::io::AsyncReadExt; +use tokio::io::AsyncWriteExt; +use tokio::runtime::Handle; +use tokio::sync::mpsc::channel; +use tokio::sync::mpsc::Receiver; +use tokio::sync::mpsc::Sender; +use tokio::sync::oneshot; +use tokio::sync::Mutex; +use tokio::sync::MutexGuard; + +pub mod api; +pub mod model; + +pub static PERMISSIONS_VARIABLE_PATTERN: Lazy = + Lazy::new(|| Regex::new(r"\{(?.+?):(?.+?)}").expect("invalid regex")); + +#[derive(Debug)] +pub enum JsMessageSide { + PluginRuntime, + Backend, +} + +static MESSAGE_ID: AtomicU32 = AtomicU32::new(0); + +pub async fn send_message(side: JsMessageSide, send: &mut SendHalf, value: T) -> anyhow::Result<()> { + let encoded: Vec = bincode::encode_to_vec(&value, bincode::config::standard())?; + + let message_id = MESSAGE_ID.fetch_add(1, Ordering::SeqCst); + + tracing::trace!( + side = debug(&side), + "Sending message with id {} and size of {} bytes: {:?}", + message_id, + encoded.len(), + &value + ); + + send.write_u32(message_id).await?; + + send.write_u32(encoded.len() as u32).await?; + + send.write_all(&encoded[..]).await?; + + tracing::trace!( + side = debug(&side), + "Message with id {} and size of {} bytes has been sent", + message_id, + encoded.len() + ); + + Ok(()) +} + +pub async fn recv_message(side: JsMessageSide, recv: &mut RecvHalf) -> anyhow::Result { + tracing::trace!(side = debug(&side), "Waiting for next message..."); + + let message_id = recv.read_u32().await?; + + tracing::trace!(side = debug(&side), "Reading message with id: {}", message_id); + + let buf_size = recv.read_u32().await?; + + let mut buffer = vec![0; buf_size as usize]; + + recv.read_exact(&mut buffer).await?; + + let (decoded, _) = bincode::decode_from_slice(&buffer[..], bincode::config::standard()) + .context(format!("Unable to deserialize message with id: {}", message_id))?; + + tracing::trace!( + side = debug(&side), + "Received message with id {}: {:?}", + message_id, + &decoded + ); + + Ok(decoded) +} diff --git a/rust/common_plugin_runtime/src/model.rs b/rust/common_plugin_runtime/src/model.rs new file mode 100644 index 0000000..46f69c1 --- /dev/null +++ b/rust/common_plugin_runtime/src/model.rs @@ -0,0 +1,213 @@ +use std::collections::HashMap; +use std::fmt; + +use bincode::Decode; +use bincode::Encode; +use gauntlet_common::model::EntrypointId; +use gauntlet_common::model::Icons; +use gauntlet_common::model::PluginId; +use gauntlet_common::model::UiWidgetId; +use serde::Deserialize; +use serde::Serialize; + +use crate::api::BackendForPluginRuntimeApiRequestData; +use crate::api::BackendForPluginRuntimeApiResponseData; + +#[derive(Debug, Deserialize, Serialize, Encode, Decode)] +#[serde(tag = "type")] +pub enum JsEvent { + OpenView { + #[serde(rename = "entrypointId")] + entrypoint_id: String, + }, + CloseView, + RunCommand { + #[serde(rename = "entrypointId")] + entrypoint_id: String, + }, + RunGeneratedEntrypoint { + #[serde(rename = "entrypointId")] + entrypoint_id: String, + #[serde(rename = "actionIndex")] + action_index: usize, + }, + ViewEvent { + #[serde(rename = "widgetId")] + widget_id: UiWidgetId, + #[serde(rename = "eventName")] + event_name: String, + #[serde(rename = "eventArguments")] + event_arguments: Vec, + }, + KeyboardEvent { + #[serde(rename = "entrypointId")] + entrypoint_id: String, + origin: JsKeyboardEventOrigin, + key: String, + #[serde(rename = "modifierShift")] + modifier_shift: bool, + #[serde(rename = "modifierControl")] + modifier_control: bool, + #[serde(rename = "modifierAlt")] + modifier_alt: bool, + #[serde(rename = "modifierMeta")] + modifier_meta: bool, + }, + OpenInlineView { + #[serde(rename = "text")] + text: String, + }, + RefreshSearchIndex, +} + +#[derive(Clone, Debug, Deserialize, Serialize, Encode, Decode)] +pub enum JsKeyboardEventOrigin { + MainView, + PluginView, +} + +// FIXME this could have been serde_v8::AnyValue but it doesn't support undefined, make a pr? +#[derive(Debug, Deserialize, Serialize, Encode, Decode)] +#[serde(tag = "type")] +pub enum JsUiPropertyValue { + String { value: String }, + Number { value: f64 }, + Bool { value: bool }, + Undefined, +} + +#[derive(Debug, Encode, Decode)] +pub enum JsMessage { + Event(JsEvent), + Response(Result), + Stop, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize, Encode, Decode)] +pub enum JsUiRenderLocation { + InlineView, + View, +} + +#[derive(Debug, Encode, Decode)] +pub struct JsPluginCode { + pub js: HashMap, +} + +#[derive(Debug, Encode, Decode)] +pub struct JsInit { + pub plugin_id: PluginId, + pub plugin_uuid: String, + pub code: JsPluginCode, + pub permissions: JsPluginPermissions, + pub inline_view_entrypoint_id: Option, + pub entrypoint_names: HashMap, + pub dev_plugin: bool, + pub home_dir: String, + pub local_storage_dir: String, + pub plugin_cache_dir: String, + pub plugin_data_dir: String, + pub stdout_file: Option, + pub stderr_file: Option, +} + +#[derive(Debug, Encode, Decode)] +pub struct JsPluginPermissions { + pub environment: Vec, + pub network: Vec, + pub filesystem: JsPluginPermissionsFileSystem, + pub exec: JsPluginPermissionsExec, + pub system: Vec, + pub main_search_bar: Vec, +} + +#[derive(Debug, Encode, Decode)] +pub struct JsPluginPermissionsFileSystem { + pub read: Vec, + pub write: Vec, +} + +#[derive(Debug, Encode, Decode)] +pub struct JsPluginPermissionsExec { + pub command: Vec, + pub executable: Vec, +} + +#[derive(Clone, Debug, Encode, Decode)] +pub enum JsPluginPermissionsMainSearchBar { + Read, +} + +#[derive(Debug, Encode, Decode)] +pub enum JsPluginRuntimeMessage { + Stopped, + Request(BackendForPluginRuntimeApiRequestData), +} + +#[derive(Encode, Decode)] +pub struct JsGeneratedSearchItem { + pub entrypoint_name: String, + pub generator_entrypoint_id: String, + pub entrypoint_id: String, + pub entrypoint_uuid: String, + pub entrypoint_icon: Option>, + pub entrypoint_actions: Vec, + pub entrypoint_accessories: Vec, +} + +impl fmt::Debug for JsGeneratedSearchItem { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + // exclude entrypoint_icon + fmt.debug_struct("JsGeneratedSearchItem") + .field("entrypoint_name", &self.entrypoint_name) + .field("generator_entrypoint_id", &self.generator_entrypoint_id) + .field("entrypoint_id", &self.entrypoint_id) + .field("entrypoint_uuid", &self.entrypoint_uuid) + .field("entrypoint_actions", &self.entrypoint_actions) + .field("entrypoint_accessories", &self.entrypoint_accessories) + .finish() + } +} + +#[derive(Debug, Deserialize, Serialize, Encode, Decode)] +pub struct JsGeneratedSearchItemAction { + pub id: Option, + pub action_type: JsGeneratedSearchItemActionType, + pub label: String, +} + +#[derive(Debug, Deserialize, Serialize, Encode, Decode)] +pub enum JsGeneratedSearchItemActionType { + View, + Command, +} + +#[derive(Debug, Deserialize, Serialize, Encode, Decode)] +#[serde(untagged)] +pub enum JsPreferenceUserData { + Number(f64), + String(String), + Bool(bool), + ListOfStrings(Vec), + ListOfNumbers(Vec), +} + +#[derive(Debug, Deserialize, Serialize, Encode, Decode)] +#[serde(untagged)] +pub enum JsGeneratedSearchItemAccessory { + TextAccessory { + text: String, + icon: Option, + tooltip: Option, + }, + IconAccessory { + icon: Icons, + tooltip: Option, + }, +} + +#[derive(Debug, Encode, Decode)] +pub struct JsClipboardData { + pub text_data: Option, + pub png_data: Option>, +} diff --git a/rust/plugin_runtime/.gitignore b/rust/plugin_runtime/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/rust/plugin_runtime/.gitignore @@ -0,0 +1 @@ +/target diff --git a/rust/plugin_runtime/Cargo.lock b/rust/plugin_runtime/Cargo.lock new file mode 100644 index 0000000..1fb556f --- /dev/null +++ b/rust/plugin_runtime/Cargo.lock @@ -0,0 +1,9259 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aead-gcm-stream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4947a169074c7e038fa43051d1c4e073f4488b0e4b0a30658f1e1a1b06449ce8" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aes-kw" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" +dependencies = [ + "aes", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +dependencies = [ + "backtrace", +] + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] + +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom 7.1.3", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure 0.12.6", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ast_node" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9184f2b369b3e8625712493c89b785881f27eedc6cde480a81883cef78868b2" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "async-compression" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +dependencies = [ + "brotli 7.0.0", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[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.101", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "attohttpc" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "184f5e6cce583a9db6b6f8d772a42cfce5b78e7c3ef26118cec3ce4c8c14969b" +dependencies = [ + "http 1.2.0", + "log", + "rustls 0.22.4", + "url", + "webpki-roots", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom 7.1.3", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide 0.8.2", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base32" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-simd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" +dependencies = [ + "simd-abstraction", +] + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref 0.5.1", + "vsimd", +] + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + +[[package]] +name = "better_scoped_tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297b153aa5e573b5863108a6ddc9d5c968bd0b20e75cc614ee9821d2f45679c7" +dependencies = [ + "scoped-tls", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bincode" +version = "2.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95" +dependencies = [ + "bincode_derive", + "serde", +] + +[[package]] +name = "bincode_derive" +version = "2.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e30759b3b99a1b802a7a3aa21c85c3ded5c28e1c83170d82d70f08bbf7f3e4c" +dependencies = [ + "virtue", +] + +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.101", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "boxed_error" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" +dependencies = [ + "quote", + "syn 2.0.101", +] + +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bstr" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "built" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "bytemuck" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "cacao" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5952f0958672e4aa8fc706d01905c56af57759e078c53a6fddf4a13361943e7a" +dependencies = [ + "block", + "core-foundation", + "core-graphics", + "dispatch", + "lazy_static", + "libc", + "objc", + "objc_id", + "os_info", + "url", +] + +[[package]] +name = "cache_control" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2a5fb3207c12b5d208ebc145f967fea5cac41a021c37417ccc31ba40f39ee" + +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.6.0", + "log", + "polling", + "rustix", + "slab", + "thiserror 1.0.69", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.52.6", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.6", +] + +[[package]] +name = "clipboard-win" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +dependencies = [ + "error-code", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "color-print" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" +dependencies = [ + "nom 7.1.3", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[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 = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cooked-waker" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cosmic-protocols" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-protocols.git#d218c76b58c7a3b20dd5e7943f93fc306a1b81b8" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "wayland-server", +] + +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto 0.2.9", + "rustc_version 0.4.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "d3d12" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" +dependencies = [ + "bitflags 2.6.0", + "libloading 0.8.6", + "winapi", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.101", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "data-url" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b319d1b62ffbd002e057f36bebd1f42b9f97927c9577461d855f3513c4289f" + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "deno_ast" +version = "0.43.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d00b724e06d2081a141ec1155756a0b465d413d8e2a7515221f61d482eb2ee" +dependencies = [ + "base64 0.21.7", + "deno_media_type", + "deno_terminal 0.1.1", + "dprint-swc-ext", + "once_cell", + "percent-encoding", + "serde", + "sourcemap 9.1.2", + "swc_atoms", + "swc_common", + "swc_config", + "swc_config_macro", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_codegen_macros", + "swc_ecma_loader", + "swc_ecma_parser", + "swc_ecma_transforms_base", + "swc_ecma_transforms_classes", + "swc_ecma_transforms_macros", + "swc_ecma_transforms_proposal", + "swc_ecma_transforms_react", + "swc_ecma_transforms_typescript", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_eq_ignore_macros", + "swc_macros_common", + "swc_visit", + "swc_visit_macros", + "text_lines", + "thiserror 1.0.69", + "unicode-width", + "url", +] + +[[package]] +name = "deno_broadcast_channel" +version = "0.173.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348ecdacfdd262e6b2f9740d07a41e8f4d79d06a670378a060515d0208495c9f" +dependencies = [ + "async-trait", + "deno_core", + "thiserror 1.0.69", + "tokio", + "uuid", +] + +[[package]] +name = "deno_cache" +version = "0.111.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6e35cb122e56c22149652327c90c563790ddcef24ea1fc77454e193131318e" +dependencies = [ + "async-trait", + "deno_core", + "rusqlite", + "serde", + "sha2", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "deno_canvas" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bbfd1437bc01ab775b1a60e3061bbf2e9517e31fb5eedf89b2b703104c835e6" +dependencies = [ + "deno_core", + "deno_webgpu", + "image 0.24.9", + "serde", + "thiserror 1.0.69", +] + +[[package]] +name = "deno_console" +version = "0.179.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e09f2bbb2d842329b602da25dbab5cd4a342f9a8adcb7c02509fc322f796e79" +dependencies = [ + "deno_core", +] + +[[package]] +name = "deno_core" +version = "0.321.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd2a54cda74cdc187d5fc2d23370a45cf09f912caf566dd1cd24a50157d809c7" +dependencies = [ + "anyhow", + "bincode 1.3.3", + "bit-set", + "bit-vec", + "bytes", + "cooked-waker", + "deno_core_icudata", + "deno_ops", + "deno_unsync", + "futures", + "indexmap 2.7.0", + "libc", + "memoffset", + "parking_lot", + "percent-encoding", + "pin-project", + "serde", + "serde_json", + "serde_v8", + "smallvec", + "sourcemap 8.0.1", + "static_assertions", + "tokio", + "url", + "v8", + "wasm_dep_analyzer", +] + +[[package]] +name = "deno_core_icudata" +version = "0.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" + +[[package]] +name = "deno_cron" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f936f036e9e3f88205db8efd0ec68c65efb47bc0cbe4b715bafecd6e9c407931" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "deno_core", + "saffron", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "deno_crypto" +version = "0.193.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b582f30887c7c0902b4445c64d7c8b98d0043ec547c44de8de26104b093e1be" +dependencies = [ + "aes", + "aes-gcm", + "aes-kw", + "base64 0.21.7", + "cbc", + "const-oid", + "ctr", + "curve25519-dalek", + "deno_core", + "deno_web", + "ed448-goldilocks", + "elliptic-curve", + "num-traits", + "once_cell", + "p256", + "p384", + "p521", + "rand", + "ring", + "rsa", + "sec1", + "serde", + "serde_bytes", + "sha1", + "sha2", + "signature", + "spki", + "thiserror 1.0.69", + "tokio", + "uuid", + "x25519-dalek", +] + +[[package]] +name = "deno_fetch" +version = "0.203.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18e66bd3bf786e24a8b8bdc97049fa82957b095a5fd1e142545c5a7cdd2272a" +dependencies = [ + "base64 0.21.7", + "bytes", + "data-url", + "deno_core", + "deno_permissions", + "deno_tls", + "dyn-clone", + "error_reporter", + "hickory-resolver", + "http 1.2.0", + "http-body-util", + "hyper 1.5.2", + "hyper-rustls", + "hyper-util", + "ipnet", + "percent-encoding", + "rustls-webpki", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tokio-rustls", + "tokio-socks", + "tokio-util", + "tower 0.4.13", + "tower-http", + "tower-service", +] + +[[package]] +name = "deno_ffi" +version = "0.166.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6d2f13ebfa93833446abeb3bd1836fdf86bcb96678276b21a0622146f42284" +dependencies = [ + "deno_core", + "deno_permissions", + "dlopen2 0.6.1", + "dynasmrt", + "libffi", + "libffi-sys", + "log", + "num-bigint", + "serde", + "serde-value", + "serde_json", + "thiserror 1.0.69", + "tokio", + "winapi", +] + +[[package]] +name = "deno_fs" +version = "0.89.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f53829328c344736d7fdda44733057299536f3379513cdcd258823ef273540ec" +dependencies = [ + "async-trait", + "base32", + "boxed_error", + "deno_core", + "deno_io", + "deno_path_util", + "deno_permissions", + "filetime", + "junction", + "libc", + "nix", + "rand", + "rayon", + "serde", + "thiserror 1.0.69", + "winapi", + "windows-sys 0.52.0", +] + +[[package]] +name = "deno_http" +version = "0.177.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b4ee6dbac20aa287a416f8905ed64b95cb484063c2af6be4eb232382c7fcb6" +dependencies = [ + "async-compression", + "async-trait", + "base64 0.21.7", + "brotli 6.0.0", + "bytes", + "cache_control", + "deno_core", + "deno_net", + "deno_websocket", + "flate2", + "http 0.2.12", + "http 1.2.0", + "httparse", + "hyper 0.14.32", + "hyper 1.5.2", + "hyper-util", + "itertools 0.10.5", + "memmem", + "mime", + "once_cell", + "percent-encoding", + "phf", + "pin-project", + "ring", + "scopeguard", + "serde", + "smallvec", + "thiserror 1.0.69", + "tokio", + "tokio-util", +] + +[[package]] +name = "deno_io" +version = "0.89.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc19195805a6b256d5ffe697c81ac79f8acd22246616fe880d6c9ec2dacf9bb4" +dependencies = [ + "async-trait", + "deno_core", + "filetime", + "fs3", + "libc", + "log", + "once_cell", + "os_pipe", + "parking_lot", + "pin-project", + "rand", + "tokio", + "uuid", + "winapi", + "windows-sys 0.52.0", +] + +[[package]] +name = "deno_kv" +version = "0.87.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a25347cd7ae561d0b05c24eebb3047e85a3af3f398675d5a9894fd167f2714f" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.21.7", + "boxed_error", + "bytes", + "chrono", + "deno_core", + "deno_fetch", + "deno_path_util", + "deno_permissions", + "deno_tls", + "denokv_proto", + "denokv_remote", + "denokv_sqlite", + "faster-hex", + "http 1.2.0", + "http-body-util", + "log", + "num-bigint", + "prost", + "prost-build", + "rand", + "rusqlite", + "serde", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "deno_media_type" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaa135b8a9febc9a51c16258e294e268a1276750780d69e46edb31cced2826e4" +dependencies = [ + "data-url", + "serde", + "url", +] + +[[package]] +name = "deno_napi" +version = "0.110.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea57b67488969f82594cb008fed1bd99830e6db042e31ee9878933d8c76be41c" +dependencies = [ + "deno_core", + "deno_permissions", + "libc", + "libloading 0.7.4", + "log", + "napi_sym", + "thiserror 1.0.69", + "windows-sys 0.52.0", +] + +[[package]] +name = "deno_native_certs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bc737e098a45aa5742d51ce694ac7236a1e69fb0d9df8c862e9b4c9583c5f9" +dependencies = [ + "dlopen2 0.7.0", + "dlopen2_derive", + "once_cell", + "rustls-native-certs", + "rustls-pemfile", +] + +[[package]] +name = "deno_net" +version = "0.171.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b3a51f7b4d5d64d17a7bc6f7495498f20d809930979d21a059d75e850cdea6" +dependencies = [ + "deno_core", + "deno_permissions", + "deno_tls", + "hickory-proto", + "hickory-resolver", + "pin-project", + "rustls-tokio-stream", + "serde", + "socket2", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "deno_node" +version = "0.116.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd0d1a757f75224e84ce8a553c2465e4a352fba4b7551ec15809d8a119847e7" +dependencies = [ + "aead-gcm-stream", + "aes", + "async-trait", + "base64 0.21.7", + "blake2", + "boxed_error", + "brotli 6.0.0", + "bytes", + "cbc", + "const-oid", + "data-encoding", + "deno_core", + "deno_fetch", + "deno_fs", + "deno_io", + "deno_media_type", + "deno_net", + "deno_package_json", + "deno_path_util", + "deno_permissions", + "deno_whoami", + "der", + "digest", + "dsa", + "ecb", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "errno 0.2.8", + "faster-hex", + "h2 0.4.7", + "hkdf", + "home", + "http 1.2.0", + "http-body-util", + "hyper 1.5.2", + "hyper-util", + "idna", + "indexmap 2.7.0", + "ipnetwork", + "k256", + "lazy-regex", + "libc", + "libz-sys", + "md-5", + "md4", + "memchr", + "node_resolver", + "num-bigint", + "num-bigint-dig", + "num-integer", + "num-traits", + "once_cell", + "p224", + "p256", + "p384", + "path-clean", + "pbkdf2", + "pin-project-lite", + "pkcs8", + "rand", + "regex", + "ring", + "ripemd", + "rsa", + "scrypt", + "sec1", + "serde", + "sha1", + "sha2", + "sha3", + "signature", + "simd-json", + "sm3", + "spki", + "stable_deref_trait", + "thiserror 1.0.69", + "tokio", + "tokio-eld", + "url", + "webpki-root-certs", + "winapi", + "windows-sys 0.52.0", + "x25519-dalek", + "x509-parser", + "yoke", +] + +[[package]] +name = "deno_ops" +version = "0.197.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a8825d92301cf445727c43f17fee2a20fcdf4370004339965156ae7c56c97e" +dependencies = [ + "proc-macro-rules", + "proc-macro2", + "quote", + "stringcase", + "strum", + "strum_macros", + "syn 2.0.101", + "thiserror 1.0.69", +] + +[[package]] +name = "deno_package_json" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbc4c4d3eb0960b58e8f43f9fc2d3f620fcac9a03cd85203e08db5b04e83c1f" +dependencies = [ + "deno_semver", + "indexmap 2.7.0", + "serde", + "serde_json", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "deno_path_util" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff25f6e08e7a0214bbacdd6f7195c7f1ebcd850c87a624e4ff06326b68b42d99" +dependencies = [ + "percent-encoding", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "deno_permissions" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e822f98185ab3ddf06104b2407681e0008af52361af32f1cd171b7eda5aa59" +dependencies = [ + "deno_core", + "deno_path_util", + "deno_terminal 0.2.0", + "fqdn", + "libc", + "log", + "once_cell", + "percent-encoding", + "serde", + "thiserror 1.0.69", + "which 4.4.2", + "winapi", +] + +[[package]] +name = "deno_runtime" +version = "0.188.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516ed4f796ab0f5dc092b5592ed6159c759f4f3a94f4a23455fecc94edc51dd1" +dependencies = [ + "async-trait", + "color-print", + "deno_ast", + "deno_broadcast_channel", + "deno_cache", + "deno_canvas", + "deno_console", + "deno_core", + "deno_cron", + "deno_crypto", + "deno_fetch", + "deno_ffi", + "deno_fs", + "deno_http", + "deno_io", + "deno_kv", + "deno_napi", + "deno_net", + "deno_node", + "deno_path_util", + "deno_permissions", + "deno_terminal 0.2.0", + "deno_tls", + "deno_url", + "deno_web", + "deno_webgpu", + "deno_webidl", + "deno_websocket", + "deno_webstorage", + "dlopen2 0.6.1", + "encoding_rs", + "fastwebsockets", + "flate2", + "http 1.2.0", + "http-body-util", + "hyper 0.14.32", + "hyper 1.5.2", + "hyper-util", + "libc", + "log", + "netif", + "nix", + "node_resolver", + "notify", + "ntapi", + "once_cell", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", + "percent-encoding", + "pin-project", + "regex", + "rustyline", + "same-file", + "serde", + "signal-hook", + "signal-hook-registry", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tokio-metrics", + "twox-hash", + "uuid", + "which 4.4.2", + "winapi", + "windows-sys 0.52.0", +] + +[[package]] +name = "deno_semver" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c957c6a57c38b7dde2315df0da0ec228911e56a74f185b108a488d0401841a67" +dependencies = [ + "monch", + "once_cell", + "serde", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "deno_terminal" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e6337d4e7f375f8b986409a76fbeecfa4bd8a1343e63355729ae4befa058eaf" +dependencies = [ + "once_cell", + "termcolor", +] + +[[package]] +name = "deno_terminal" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daef12499e89ee99e51ad6000a91f600d3937fb028ad4918af76810c5bc9e0d5" +dependencies = [ + "once_cell", + "termcolor", +] + +[[package]] +name = "deno_tls" +version = "0.166.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688175eed35e7b3053ec114227894ef24786855405d8844058a48bffa997d85a" +dependencies = [ + "deno_core", + "deno_native_certs", + "rustls 0.23.20", + "rustls-pemfile", + "rustls-tokio-stream", + "rustls-webpki", + "serde", + "thiserror 1.0.69", + "tokio", + "webpki-roots", +] + +[[package]] +name = "deno_unsync" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d774fd83f26b24f0805a6ab8b26834a0d06ceac0db517b769b1e4633c96a2057" +dependencies = [ + "futures", + "parking_lot", + "tokio", +] + +[[package]] +name = "deno_url" +version = "0.179.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9a108794e505f2b07665e19ff336c1bcba6adcf7182c90c1d3a6c741d7fcd0" +dependencies = [ + "deno_core", + "thiserror 1.0.69", + "urlpattern", +] + +[[package]] +name = "deno_web" +version = "0.210.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7679087bcc41f7ae3385f8c12d43bc81cfc54cb9b1ef73983d20f5e39fa4e0da" +dependencies = [ + "async-trait", + "base64-simd 0.8.0", + "bytes", + "deno_core", + "deno_permissions", + "encoding_rs", + "flate2", + "futures", + "serde", + "thiserror 1.0.69", + "tokio", + "uuid", +] + +[[package]] +name = "deno_webgpu" +version = "0.146.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f78b73638be1552b31778e42267f4fb47e902f7b261bdb0f951ba2b1d6bfab" +dependencies = [ + "deno_core", + "raw-window-handle", + "serde", + "thiserror 1.0.69", + "tokio", + "wgpu-core", + "wgpu-types", +] + +[[package]] +name = "deno_webidl" +version = "0.179.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b55d845e3d64f8de7eff67aaa4b6fe1b23bbc2efe967c984f8c64c8dd85fad4" +dependencies = [ + "deno_core", +] + +[[package]] +name = "deno_websocket" +version = "0.184.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00407052c6524828f2708557c47059ba9b87874758416c66f47f5102ac68422" +dependencies = [ + "bytes", + "deno_core", + "deno_net", + "deno_permissions", + "deno_tls", + "fastwebsockets", + "h2 0.4.7", + "http 1.2.0", + "http-body-util", + "hyper 1.5.2", + "hyper-util", + "once_cell", + "rustls-tokio-stream", + "serde", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "deno_webstorage" +version = "0.174.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecaabbb1580d21811642f11cc12fe8599684efeb9398eaa998a3db8811e8edc" +dependencies = [ + "deno_core", + "deno_web", + "rusqlite", + "thiserror 1.0.69", +] + +[[package]] +name = "deno_whoami" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75e4caa92b98a27f09c671d1399aee0f5970aa491b9a598523aac000a2192e3" +dependencies = [ + "libc", + "whoami", +] + +[[package]] +name = "denokv_proto" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7ba1f99ed11a9c11e868a8521b1f71a7e1aba785d7f42ea9ecbdc01146c89ec" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "futures", + "num-bigint", + "prost", + "serde", + "uuid", +] + +[[package]] +name = "denokv_remote" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ed833073189e8f6d03155fe3b05a024e75e29d8a28a4c2e9ec3b5c925e727b" +dependencies = [ + "anyhow", + "async-stream", + "async-trait", + "bytes", + "chrono", + "denokv_proto", + "futures", + "http 1.2.0", + "log", + "prost", + "rand", + "serde", + "serde_json", + "tokio", + "tokio-util", + "url", + "uuid", +] + +[[package]] +name = "denokv_sqlite" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b790f01d1302d53a0c3cbd27de88a06b3abd64ec8ab8673924e490541c7c713" +dependencies = [ + "anyhow", + "async-stream", + "async-trait", + "chrono", + "denokv_proto", + "futures", + "hex", + "log", + "num-bigint", + "rand", + "rusqlite", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tokio-stream", + "uuid", + "v8_valueserializer", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "der_derive", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom 7.1.3", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.6", +] + +[[package]] +name = "dlopen2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bc2c7ed06fd72a8513ded8d0d2f6fd2655a85d6885c48cae8625d80faf28c03" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dprint-swc-ext" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba28c12892aadb751c2ba7001d8460faee4748a04b4edc51c7121cc67ee03db" +dependencies = [ + "num-bigint", + "rustc-hash 1.1.0", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "text_lines", +] + +[[package]] +name = "dsa" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" +dependencies = [ + "digest", + "num-bigint-dig", + "num-traits", + "pkcs8", + "rfc6979", + "sha2", + "signature", + "zeroize", +] + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2 0.5.10", +] + +[[package]] +name = "ecb" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" +dependencies = [ + "cipher", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "signature", + "subtle", + "zeroize", +] + +[[package]] +name = "ed448-goldilocks" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06924531e9e90130842b012e447f85bdaf9161bc8a0f8092be8cb70b01ebe092" +dependencies = [ + "fiat-crypto 0.1.20", + "hex", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "base64ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "serde_json", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" +dependencies = [ + "encoding-index-japanese", + "encoding-index-korean", + "encoding-index-simpchinese", + "encoding-index-singlebyte", + "encoding-index-tradchinese", +] + +[[package]] +name = "encoding-index-japanese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-korean" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-simpchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-singlebyte" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-tradchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding_index_tests" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "error-code" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" + +[[package]] +name = "error_reporter" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ae425815400e5ed474178a7a22e275a9687086a12ca63ec793ff292d8fdae8" + +[[package]] +name = "exr" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide 0.8.2", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "faster-hex" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" +dependencies = [ + "serde", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fastwebsockets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26da0c7b5cef45c521a6f9cdfffdfeb6c9f5804fbac332deb5ae254634c7a6be" +dependencies = [ + "base64 0.21.7", + "bytes", + "http-body-util", + "hyper 1.5.2", + "hyper-util", + "pin-project", + "rand", + "sha1", + "simdutf8", + "thiserror 1.0.69", + "tokio", + "utf-8", +] + +[[package]] +name = "fd-lock" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide 0.8.2", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fqdn" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb540cf7bc4fe6df9d8f7f0c974cfd0dce8ed4e9e8884e73433b503ee78b4e7d" + +[[package]] +name = "freedesktop-icons" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8ef34245e0540c9a3ce7a28340b98d2c12b75da0d446da4e8224923fcaa0c16" +dependencies = [ + "dirs", + "once_cell", + "rust-ini", + "thiserror 1.0.69", + "xdg", +] + +[[package]] +name = "freedesktop_entry_parser" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db9c27b72f19a99a895f8ca89e2d26e4ef31013376e56fdafef697627306c3e4" +dependencies = [ + "nom 7.1.3", + "thiserror 1.0.69", +] + +[[package]] +name = "from_variant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" +dependencies = [ + "proc-macro2", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "fs3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb17cf6ed704f72485332f6ab65257460c4f9f3083934cf402bf9f5b3b600a90" +dependencies = [ + "libc", + "rustc_version 0.2.3", + "winapi", +] + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "fslock" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gauntlet-common" +version = "0.0.0" +dependencies = [ + "anyhow", + "base64 0.22.1", + "bincode 2.0.0-rc.3", + "bytes", + "convert_case 0.6.0", + "directories", + "gauntlet-component-model", + "gauntlet-utils", + "gauntlet-utils-macros", + "gix-url", + "indexmap 2.7.0", + "itertools 0.13.0", + "libc", + "prost", + "serde", + "serde_json", + "thiserror 2.0.8", + "tokio", + "tonic", + "tonic-build", +] + +[[package]] +name = "gauntlet-common-plugin-runtime" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode 2.0.0-rc.3", + "gauntlet-common", + "gauntlet-utils", + "gauntlet-utils-macros", + "interprocess", + "once_cell", + "regex", + "serde", + "tokio", + "tracing", +] + +[[package]] +name = "gauntlet-component-model" +version = "0.0.0" +dependencies = [ + "anyhow", + "indexmap 2.7.0", + "serde", + "serde_json", +] + +[[package]] +name = "gauntlet-plugin-runtime" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode 2.0.0-rc.3", + "bytes", + "cacao", + "cosmic-protocols", + "deno_core", + "deno_runtime", + "encoding", + "freedesktop-icons", + "freedesktop_entry_parser", + "futures", + "gauntlet-common", + "gauntlet-common-plugin-runtime", + "gauntlet-component-model", + "gauntlet-utils", + "icns", + "image 0.25.5", + "indexmap 2.7.0", + "interprocess", + "libc", + "numbat", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", + "open", + "plist", + "regex", + "resvg", + "serde", + "smithay-client-toolkit", + "sys-locale", + "tokio", + "tokio-util", + "tracing", + "typed-path", + "uuid", + "walkdir", + "wayland-client", + "wayland-protocols-wlr", + "which 7.0.1", + "windows", + "x11rb", +] + +[[package]] +name = "gauntlet-utils" +version = "0.0.0" +dependencies = [ + "anyhow", + "prost", + "thiserror 2.0.8", + "tokio", + "tonic", +] + +[[package]] +name = "gauntlet-utils-macros" +version = "0.1.0" +dependencies = [ + "convert_case 0.8.0", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "gix-features" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d85d673f2e022a340dba4713bed77ef2cf4cd737d2f3e0f159d45e0935fd81f" +dependencies = [ + "gix-hash", + "gix-trace", + "libc", +] + +[[package]] +name = "gix-hash" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5eccc17194ed0e67d49285e4853307e4147e95407f91c1c3e4a13ba9f4e4ce" +dependencies = [ + "faster-hex", + "thiserror 2.0.8", +] + +[[package]] +name = "gix-path" +version = "0.10.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc292ef1a51e340aeb0e720800338c805975724c1dfbd243185452efd8645b7" +dependencies = [ + "bstr", + "gix-trace", + "home", + "once_cell", + "thiserror 2.0.8", +] + +[[package]] +name = "gix-trace" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04bdde120c29f1fc23a24d3e115aeeea3d60d8e65bab92cc5f9d90d9302eb952" + +[[package]] +name = "gix-url" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e09f97db3618fb8e473d7d97e77296b50aaee0ddcd6a867f07443e3e87391099" +dependencies = [ + "bstr", + "gix-features", + "gix-path", + "thiserror 2.0.8", + "url", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "glow" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.6.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" +dependencies = [ + "bitflags 2.6.0", + "gpu-descriptor-types", + "hashbrown 0.15.2", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "gzip-header" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" +dependencies = [ + "crc32fast", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.7.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.2.0", + "indexmap 2.7.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "halfbrown" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8588661a8607108a5ca69cab034063441a0413a0b041c13618a7dd348021ef6f" +dependencies = [ + "hashbrown 0.14.5", + "serde", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "base64 0.21.7", + "byteorder", + "crossbeam-channel", + "flate2", + "nom 7.1.3", + "num-traits", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "hickory-proto" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna", + "ipnet", + "once_cell", + "rand", + "serde", + "thiserror 1.0.69", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "serde", + "smallvec", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "hstr" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412" +dependencies = [ + "hashbrown 0.14.5", + "new_debug_unreachable", + "once_cell", + "phf", + "rustc-hash 1.1.0", + "triomphe", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.2.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6884a48c6826ec44f524c7456b163cebe9e55a18d7b5e307cb4f100371cc767" +dependencies = [ + "futures-util", + "http 1.2.0", + "hyper 1.5.2", + "hyper-util", + "rustls 0.23.20", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +dependencies = [ + "hyper 1.5.2", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.5.2", + "pin-project-lite", + "socket2", + "tokio", + "tower 0.4.13", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icns" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ccfbad7e08da70a5b48a924994a5afd93125ce5d45a3b0ba0b8da7bda59a40" +dependencies = [ + "byteorder", + "png 0.16.8", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-traits", + "png 0.17.16", +] + +[[package]] +name = "image" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png 0.17.16", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" +dependencies = [ + "byteorder-lite", + "quick-error 2.0.1", +] + +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "interprocess" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "894148491d817cb36b6f778017b8ac46b17408d522dd90f539d677ea938362eb" +dependencies = [ + "doctest-file", + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.52.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] + +[[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-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[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 = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "jiff" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db69f08d4fb10524cacdb074c10b296299d71274ddbc830a8ee65666867002e9" +dependencies = [ + "jiff-tzdb-platform", + "js-sys", + "wasm-bindgen", + "windows-sys 0.59.0", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91335e575850c5c4c673b9bd467b0e025f164ca59d0564f69d0c2ee0ffad4653" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9835f0060a626fe59f160437bc725491a6af23133ea906500027d1bd2f8f4329" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "junction" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be39922b087cecaba4e2d5592dedfc8bda5d4a5a1231f143337cca207950b61d" +dependencies = [ + "scopeguard", + "winapi", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading 0.8.6", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "kurbo" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f" +dependencies = [ + "arrayvec", + "smallvec", +] + +[[package]] +name = "lazy-regex" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d8e41c97e6bc7ecb552016274b99fbb5d035e8de288c582d9b933af6677bfda" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e1d8b05d672c53cb9c7b920bbba8783845ae4f0b076e02a3db1d02c81b4163" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.101", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libffi" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2" +dependencies = [ + "libc", + "libffi-sys", +] + +[[package]] +name = "libffi-sys" +version = "2.3.0" +source = "git+https://github.com/tov/libffi-rs?rev=d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b#d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b" +dependencies = [ + "cc", +] + +[[package]] +name = "libfuzzer-sys" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "md4" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da5ac363534dce5fabf69949225e174fbf111a498bf0ff794c8ea1fba9f3dda" +dependencies = [ + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "memmem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mendeleev" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8dd6ec5207f7f69db7abb42466511394956dc85faf163de1fe393246c8b7e4" +dependencies = [ + "serde", +] + +[[package]] +name = "metal" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" +dependencies = [ + "bitflags 2.6.0", + "block", + "core-graphics-types", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "monch" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52c1b33ff98142aecea13138bd399b68aa7ab5d9546c300988c345004001eea" + +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + +[[package]] +name = "naga" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags 2.6.0", + "codespan-reporting", + "hexf-parse", + "indexmap 2.7.0", + "log", + "num-traits", + "rustc-hash 1.1.0", + "serde", + "spirv", + "termcolor", + "thiserror 1.0.69", + "unicode-xid", +] + +[[package]] +name = "napi_sym" +version = "0.109.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b3ee1b2d30885de3ee82429b5aebe6f22b3eae5cb290cd8d6537a62212812b" +dependencies = [ + "quote", + "serde", + "serde_json", + "syn 2.0.101", +] + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "netif" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29a01b9f018d6b7b277fef6c79fdbd9bf17bb2d1e298238055cafab49baa5ee" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "libc", +] + +[[package]] +name = "node_resolver" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e999e1cdbb49cdfa3f63ddd061c57205aa5f7be8f43bdbc4081c0f60d24d7d" +dependencies = [ + "anyhow", + "async-trait", + "boxed_error", + "deno_media_type", + "deno_package_json", + "deno_path_util", + "futures", + "lazy-regex", + "once_cell", + "path-clean", + "regex", + "serde_json", + "thiserror 1.0.69", + "tokio", + "url", +] + +[[package]] +name = "nom" +version = "5.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" +dependencies = [ + "memchr", + "version_check", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.6.0", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio 0.8.11", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "rand", + "serde", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "serde", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "numbat" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5124c7a716bd197d4ad501237fa890771f69f38b34eb87f4514fdebf0cdcaf5b" +dependencies = [ + "codespan-reporting", + "heck 0.4.1", + "indexmap 2.7.0", + "itertools 0.12.1", + "jiff", + "libc", + "mendeleev", + "num-format", + "num-integer", + "num-rational", + "num-traits", + "numbat-exchange-rates", + "plotly", + "pretty_dtoa", + "rand", + "rust-embed", + "strfmt", + "strsim", + "thiserror 1.0.69", + "unicode-ident", + "unicode-width", + "walkdir", +] + +[[package]] +name = "numbat-exchange-rates" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd1e3c3e4f9f22d0d7cdcb413f01194f6506a302a9029d95deedcd1c25df7718" +dependencies = [ + "attohttpc", + "quick-xml 0.31.0", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "open" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ecd52f0b8d15c40ce4820aa251ed5de032e5d91fab27f7db2f40d42a8bdf69c" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "opentelemetry" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab70038c28ed37b97d8ed414b6429d343a8bbf44c9f79ec854f3a643029ba6d7" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 1.0.69", + "tracing", +] + +[[package]] +name = "opentelemetry-http" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a8a7f5f6ba7c1b286c2fbca0454eaba116f63bbe69ed250b642d36fbb04d80" +dependencies = [ + "async-trait", + "bytes", + "http 1.2.0", + "opentelemetry", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76" +dependencies = [ + "async-trait", + "futures-core", + "http 1.2.0", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tonic", + "tracing", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e05acbfada5ec79023c85368af14abd0b307c015e9064d249b2a950ef459a6" +dependencies = [ + "hex", + "opentelemetry", + "opentelemetry_sdk", + "prost", + "serde", + "tonic", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc1b6902ff63b32ef6c489e8048c5e253e2e4a803ea3ea7e783914536eb15c52" + +[[package]] +name = "opentelemetry_sdk" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8" +dependencies = [ + "async-trait", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "opentelemetry", + "percent-encoding", + "rand", + "serde_json", + "thiserror 1.0.69", + "tracing", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + +[[package]] +name = "os_info" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ca711d8b83edbb00b44d504503cd247c9c0bd8b0fa2694f2a1a3d8165379ce" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + +[[package]] +name = "os_pipe" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "outref" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "p224" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c06436d66652bc2f01ade021592c80a2aad401570a18aa18b82e440d2b9aa1" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core", + "sha2", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "path-clean" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.7.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs5" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" +dependencies = [ + "aes", + "cbc", + "der", + "pbkdf2", + "scrypt", + "sha2", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "pkcs5", + "rand_core", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "plist" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +dependencies = [ + "base64 0.22.1", + "indexmap 2.7.0", + "quick-xml 0.32.0", + "serde", + "time", +] + +[[package]] +name = "plotly" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e1ffd11c8a6ef0b730b9d3e46ad2404f79905825cb20223fa0547434a2dff54" +dependencies = [ + "dyn-clone", + "erased-serde", + "once_cell", + "plotly_derive", + "rand", + "rinja", + "serde", + "serde_json", + "serde_repr", + "serde_with", +] + +[[package]] +name = "plotly_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e940d8d8db30c6f4cc37dab9aab61f4c9cc1e6efb6d18902ab88fa09c03560" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.2", +] + +[[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 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "pretty_dtoa" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a239bcdfda2c685fda1add3b4695c06225f50075e3cfb5b954e91545587edff2" +dependencies = [ + "ryu_floating_decimal", +] + +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.101", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-rules" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" +dependencies = [ + "proc-macro-rules-macros", + "proc-macro2", + "syn 2.0.101", +] + +[[package]] +name = "proc-macro-rules-macros" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207fffb0fe655d1d47f6af98cc2793405e85929bdbc420d685554ff07be27ac7" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +dependencies = [ + "quote", + "syn 2.0.101", +] + +[[package]] +name = "prost" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" +dependencies = [ + "heck 0.5.0", + "itertools 0.13.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.101", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "prost-types" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" +dependencies = [ + "prost", +] + +[[package]] +name = "psm" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" +dependencies = [ + "cc", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "range-alloc" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.12.1", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error 2.0.1", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error 1.2.3", +] + +[[package]] +name = "resvg" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958" +dependencies = [ + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rinja" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5" +dependencies = [ + "humansize", + "itoa", + "percent-encoding", + "rinja_derive", + "serde", + "serde_json", +] + +[[package]] +name = "rinja_derive" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b" +dependencies = [ + "basic-toml", + "memchr", + "mime", + "mime_guess", + "proc-macro2", + "quote", + "rinja_parser", + "rustc-hash 2.1.0", + "serde", + "syn 2.0.101", +] + +[[package]] +name = "rinja_parser" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610" +dependencies = [ + "memchr", + "nom 7.1.3", + "serde", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64 0.21.7", + "bitflags 2.6.0", + "serde", + "serde_derive", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rsa" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags 2.6.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rust-embed" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "shellexpand", + "syn 2.0.101", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rust-ini" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.24", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags 2.6.0", + "errno 0.3.10", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls" +version = "0.23.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + +[[package]] +name = "rustls-tokio-stream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22557157d7395bc30727745b365d923f1ecc230c4c80b176545f3f4f08c46e33" +dependencies = [ + "futures", + "rustls 0.23.20", + "socket2", + "tokio", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "rustyline" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "ryu-js" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" + +[[package]] +name = "ryu_floating_decimal" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "700de91d5fd6091442d00fdd9ee790af6d4f0f480562b0f5a1e8f59e90aafe73" + +[[package]] +name = "saffron" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03fb9a628596fc7590eb7edbf7b0613287be78df107f5f97b118aad59fb2eea9" +dependencies = [ + "chrono", + "nom 5.1.3", +] + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "password-hash", + "pbkdf2", + "salsa20", + "sha2", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "indexmap 2.7.0", + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_v8" +version = "0.230.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a783242d2af51d6955cc04bf2b64adb643ab588b61e9573c908a69dabf8c2f" +dependencies = [ + "num-bigint", + "serde", + "smallvec", + "thiserror 1.0.69", + "v8", +] + +[[package]] +name = "serde_with" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shellexpand" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +dependencies = [ + "dirs", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[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 = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "simd-abstraction" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" +dependencies = [ + "outref 0.1.0", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd-json" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2bcf6c6e164e81bc7a5d49fc6988b3d515d9e8c07457d7b74ffb9324b9cd40" +dependencies = [ + "getrandom", + "halfbrown", + "ref-cast", + "serde", + "serde_json", + "simdutf8", + "value-trait", +] + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "sm3" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebb9a3b702d0a7e33bc4d85a14456633d2b165c2ad839c5fd9a8417c1ab15860" +dependencies = [ + "digest", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.6.0", + "bytemuck", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2 0.9.5", + "pkg-config", + "rustix", + "thiserror 1.0.69", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkbcommon", + "xkeysym", +] + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "sourcemap" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" +dependencies = [ + "base64-simd 0.7.0", + "bitvec", + "data-encoding", + "debugid", + "if_chain", + "rustc-hash 1.1.0", + "rustc_version 0.2.3", + "serde", + "serde_json", + "unicode-id-start", + "url", +] + +[[package]] +name = "sourcemap" +version = "9.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c4ea7042fd1a155ad95335b5d505ab00d5124ea0332a06c8390d200bb1a76a" +dependencies = [ + "base64-simd 0.7.0", + "bitvec", + "data-encoding", + "debugid", + "if_chain", + "rustc-hash 1.1.0", + "rustc_version 0.2.3", + "serde", + "serde_json", + "unicode-id-start", + "url", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strfmt" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a8348af2d9fc3258c8733b8d9d8db2e56f54b2363a4b5b81585c7875ed65e65" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp 0.9.0", +] + +[[package]] +name = "string_enum" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "stringcase" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04028eeb851ed08af6aba5caa29f2d59a13ed168cee4d6bd753aeefcf1d636b0" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.101", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "svgtypes" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794de53cc48eaabeed0ab6a3404a65f40b3e38c067e4435883a65d2aa4ca000e" +dependencies = [ + "kurbo", + "siphasher 1.0.1", +] + +[[package]] +name = "swc_allocator" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76aa0eb65c0f39f9b6d82a7e5192c30f7ac9a78f084a21f270de1d8c600ca388" +dependencies = [ + "bumpalo", + "hashbrown 0.14.5", + "ptr_meta", + "rustc-hash 1.1.0", + "triomphe", +] + +[[package]] +name = "swc_atoms" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6567e4e67485b3e7662b486f1565bdae54bd5b9d6b16b2ba1a9babb1e42125" +dependencies = [ + "hstr", + "once_cell", + "rustc-hash 1.1.0", + "serde", +] + +[[package]] +name = "swc_cached" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c" +dependencies = [ + "ahash", + "anyhow", + "dashmap", + "once_cell", + "regex", + "serde", +] + +[[package]] +name = "swc_common" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12d0a8eaaf1606c9207077d75828008cb2dfb51b095a766bd2b72ef893576e31" +dependencies = [ + "ast_node", + "better_scoped_tls", + "cfg-if", + "either", + "from_variant", + "new_debug_unreachable", + "num-bigint", + "once_cell", + "rustc-hash 1.1.0", + "serde", + "siphasher 0.3.11", + "sourcemap 9.1.2", + "swc_allocator", + "swc_atoms", + "swc_eq_ignore_macros", + "swc_visit", + "tracing", + "unicode-width", + "url", +] + +[[package]] +name = "swc_config" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" +dependencies = [ + "anyhow", + "indexmap 2.7.0", + "serde", + "serde_json", + "swc_cached", + "swc_config_macro", +] + +[[package]] +name = "swc_config_macro" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c5f56139042c1a95b54f5ca48baa0e0172d369bcc9d3d473dad1de36bae8399" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "swc_ecma_ast" +version = "0.118.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6f866d12e4d519052b92a0a86d1ac7ff17570da1272ca0c89b3d6f802cd79df" +dependencies = [ + "bitflags 2.6.0", + "is-macro", + "num-bigint", + "phf", + "scoped-tls", + "serde", + "string_enum", + "swc_atoms", + "swc_common", + "unicode-id-start", +] + +[[package]] +name = "swc_ecma_codegen" +version = "0.155.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7641608ef117cfbef9581a99d02059b522fcca75e5244fa0cbbd8606689c6f" +dependencies = [ + "memchr", + "num-bigint", + "once_cell", + "serde", + "sourcemap 9.1.2", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_codegen_macros", + "tracing", +] + +[[package]] +name = "swc_ecma_codegen_macros" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859fabde36db38634f3fad548dd5e3410c1aebba1b67a3c63e67018fa57a0bca" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "swc_ecma_loader" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55fa3d55045b97894bfb04d38aff6d6302ac8a6a38e3bb3dfb0d20475c4974a9" +dependencies = [ + "anyhow", + "pathdiff", + "serde", + "swc_atoms", + "swc_common", + "tracing", +] + +[[package]] +name = "swc_ecma_parser" +version = "0.149.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683dada14722714588b56481399c699378b35b2ba4deb5c4db2fb627a97fb54b" +dependencies = [ + "either", + "new_debug_unreachable", + "num-bigint", + "num-traits", + "phf", + "serde", + "smallvec", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "tracing", + "typed-arena", +] + +[[package]] +name = "swc_ecma_transforms_base" +version = "0.145.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65f21494e75d0bd8ef42010b47cabab9caaed8f2207570e809f6f4eb51a710d1" +dependencies = [ + "better_scoped_tls", + "bitflags 2.6.0", + "indexmap 2.7.0", + "once_cell", + "phf", + "rustc-hash 1.1.0", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_utils", + "swc_ecma_visit", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_classes" +version = "0.134.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3d884594385bea9405a2e1721151470d9a14d3ceec5dd773c0ca6894791601" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_macros" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500a1dadad1e0e41e417d633b3d6d5de677c9e0d3159b94ba3348436cdb15aab" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "swc_ecma_transforms_proposal" +version = "0.179.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79938ff510fc647febd8c6c3ef4143d099fdad87a223680e632623d056dae2dd" +dependencies = [ + "either", + "rustc-hash 1.1.0", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_transforms_classes", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_react" +version = "0.191.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" +dependencies = [ + "base64 0.21.7", + "dashmap", + "indexmap 2.7.0", + "once_cell", + "serde", + "sha1", + "string_enum", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_transforms_base", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_typescript" +version = "0.198.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15455da4768f97186c40523e83600495210c11825d3a44db43383fd81eace88d" +dependencies = [ + "ryu-js", + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_transforms_react", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_utils" +version = "0.134.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029eec7dd485923a75b5a45befd04510288870250270292fc2c1b3a9e7547408" +dependencies = [ + "indexmap 2.7.0", + "num_cpus", + "once_cell", + "rustc-hash 1.1.0", + "ryu-js", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_visit", + "tracing", + "unicode-id", +] + +[[package]] +name = "swc_ecma_visit" +version = "0.104.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b1c6802e68e51f336e8bc9644e9ff9da75d7da9c1a6247d532f2e908aa33e81" +dependencies = [ + "new_debug_unreachable", + "num-bigint", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_visit", + "tracing", +] + +[[package]] +name = "swc_eq_ignore_macros" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "swc_macros_common" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "swc_visit" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ceb044142ba2719ef9eb3b6b454fce61ab849eb696c34d190f04651955c613d" +dependencies = [ + "either", + "new_debug_unreachable", +] + +[[package]] +name = "swc_visit_macros" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92807d840959f39c60ce8a774a3f83e8193c658068e6d270dbe0a05e40e90b41" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "text_lines" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd5828de7deaa782e1dd713006ae96b3bee32d3279b79eb67ecf8072c059bcf" +dependencies = [ + "serde", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" +dependencies = [ + "thiserror-impl 2.0.8", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png 0.17.16", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio 1.0.3", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-eld" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9166030f05d6bc5642bdb8f8c2be31eb3c02cd465d662bcdc2df82d4aa41a584" +dependencies = [ + "hdrhistogram", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "tokio-metrics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eace09241d62c98b7eeb1107d4c5c64ca3bd7da92e8c218c153ab3a78f9be112" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls 0.23.20", + "tokio", +] + +[[package]] +name = "tokio-socks" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" +dependencies = [ + "either", + "futures-util", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "futures-util", + "hashbrown 0.14.5", + "pin-project-lite", + "slab", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap 2.7.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.22.1", + "bytes", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.2", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +dependencies = [ + "async-compression", + "bitflags 2.6.0", + "bytes", + "futures-core", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +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.101", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "triomphe" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +dependencies = [ + "serde", + "stable_deref_trait", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "rand", + "static_assertions", +] + +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + +[[package]] +name = "typed-path" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41713888c5ccfd99979fcd1afd47b71652e331b3d4a0e19d30769e80fec76cce" + +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicase" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" + +[[package]] +name = "unicode-id" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" + +[[package]] +name = "unicode-id-start" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "usvg" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447e703d7223b067607655e625e0dbca80822880248937da65966194c4864e6" +dependencies = [ + "base64 0.22.1", + "data-url", + "flate2", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree", + "simplecss", + "siphasher 1.0.1", + "strict-num", + "svgtypes", + "tiny-skia-path", + "xmlwriter", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "v8" +version = "130.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee0be58935708fa4d7efb970c6cf9f2d9511d24ee24246481a65b6ee167348d" +dependencies = [ + "bindgen", + "bitflags 2.6.0", + "fslock", + "gzip-header", + "home", + "miniz_oxide 0.7.4", + "once_cell", + "paste", + "which 6.0.3", +] + +[[package]] +name = "v8_valueserializer" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97599c400fc79925922b58303e98fcb8fa88f573379a08ddb652e72cbd2e70f6" +dependencies = [ + "bitflags 2.6.0", + "encoding_rs", + "indexmap 2.7.0", + "num-bigint", + "serde", + "thiserror 1.0.69", + "wtf8", +] + +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "value-trait" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9170e001f458781e92711d2ad666110f153e4e50bfd5cbd02db6547625714187" +dependencies = [ + "float-cmp 0.10.0", + "halfbrown", + "itoa", + "ryu", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "virtue" +version = "0.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.101", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + +[[package]] +name = "wasm_dep_analyzer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f270206a91783fd90625c8bb0d8fbd459d0b1d1bf209b656f713f01ae7c04b8" +dependencies = [ + "thiserror 1.0.69", +] + +[[package]] +name = "wayland-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" +dependencies = [ + "bitflags 2.6.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.6.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b08bc3aafdb0035e7fe0fdf17ba0c09c268732707dca4ae098f60cb28c9e4c" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", + "wayland-server", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", + "wayland-server", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +dependencies = [ + "proc-macro2", + "quick-xml 0.36.2", + "quote", +] + +[[package]] +name = "wayland-server" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89532cc712a2adb119eb4d09694b402576052254d0bb284f82ac1c47fb786ad" +dependencies = [ + "bitflags 2.6.0", + "downcast-rs", + "io-lifetimes", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-sys" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd5da49bdf1f30054cfe0b8ce2958b8fbeb67c4d82c8967a598af481bef255c" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "wgpu-core" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39" +dependencies = [ + "arrayvec", + "bit-vec", + "bitflags 2.6.0", + "cfg_aliases", + "codespan-reporting", + "document-features", + "indexmap 2.7.0", + "log", + "naga", + "once_cell", + "parking_lot", + "profiling", + "raw-window-handle", + "ron", + "rustc-hash 1.1.0", + "serde", + "smallvec", + "thiserror 1.0.69", + "web-sys", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.6.0", + "block", + "cfg_aliases", + "core-graphics-types", + "d3d12", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-descriptor", + "js-sys", + "khronos-egl", + "libc", + "libloading 0.8.6", + "log", + "metal", + "naga", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "profiling", + "range-alloc", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", +] + +[[package]] +name = "wgpu-types" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" +dependencies = [ + "bitflags 2.6.0", + "js-sys", + "serde", + "web-sys", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "which" +version = "6.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" +dependencies = [ + "either", + "home", + "rustix", + "winsafe", +] + +[[package]] +name = "which" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a9e33648339dc1642b0e36e21b3385e6148e289226f657c809dee59df5028" +dependencies = [ + "either", + "env_home", + "rustix", + "winsafe", +] + +[[package]] +name = "whoami" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall", + "wasite", + "web-sys", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wtf8" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01ae8492c38f52376efd3a17d0994b6bcf3df1e39c0226d458b7d81670b2a06" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom 7.1.3", + "oid-registry", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "xcursor" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" + +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" + +[[package]] +name = "xkbcommon" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +dependencies = [ + "libc", + "memmap2 0.8.0", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "xml-rs" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "synstructure 0.13.1", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "synstructure 0.13.1", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] diff --git a/rust/plugin_runtime/Cargo.toml b/rust/plugin_runtime/Cargo.toml index b619d79..92c608b 100644 --- a/rust/plugin_runtime/Cargo.toml +++ b/rust/plugin_runtime/Cargo.toml @@ -3,29 +3,35 @@ name = "gauntlet-plugin-runtime" version = "0.1.0" edition.workspace = true +[workspace.package] +edition = "2021" + +[lib] +crate-type = ["rlib"] + [dependencies] # workspaces -gauntlet-common.workspace = true -gauntlet-component-model.workspace = true -gauntlet-utils.workspace = true -gauntlet-utils-macros.workspace = true +gauntlet-common = { path = "../common" } +gauntlet-component-model = { path = "../component_model" } +gauntlet-utils = { path = "../utils" } +gauntlet-common-plugin-runtime = { path = "../common_plugin_runtime" } # shared -anyhow.workspace = true -tokio.workspace = true -tokio-util.workspace = true -serde.workspace = true -tracing.workspace = true -indexmap.workspace = true -bincode.workspace = true -regex.workspace = true -futures.workspace = true -image.workspace = true -once_cell.workspace = true -bytes.workspace = true -walkdir.workspace = true -typed-path.workspace = true -interprocess.workspace = true +anyhow = { version = "1", features = ["backtrace"] } +tracing = { version = "0.1" } +tokio = { version = "1.42" } +tokio-util = "0.7" +serde = { version = "1.0", features = ["derive"] } +bincode = { version = "2.0.0-rc.3" } +indexmap = { version = "2.1", features = ["serde"] } +regex = { version = "1.9.3" } +futures = { version = "0.3.31" } +image = { version = "0.25" } +once_cell = { version = "1.19" } +bytes = { version = "1.6.0" } +walkdir = { version = "2.4.0" } +typed-path = { version = "0.10.0" } +interprocess = { version = "2.2.2", features = ["tokio"] } # other deno_core = { version = "0.321.0" } # deno 2.1.1 @@ -64,3 +70,7 @@ libc = "0.2" [features] scenario_runner = [] release = [] + +[patch.crates-io] +# NOTE https://github.com/ipetkov/crane/issues/336 +libffi-sys = { git = "https://github.com/tov/libffi-rs", rev = "d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b" } diff --git a/rust/plugin_runtime/src/assets.rs b/rust/plugin_runtime/src/assets.rs index 9c523b6..d611e17 100644 --- a/rust/plugin_runtime/src/assets.rs +++ b/rust/plugin_runtime/src/assets.rs @@ -4,9 +4,8 @@ use std::rc::Rc; use deno_core::futures::executor::block_on; use deno_core::op2; use deno_core::OpState; - -use crate::api::BackendForPluginRuntimeApi; -use crate::api::BackendForPluginRuntimeApiProxy; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; #[op2(async)] #[buffer] diff --git a/rust/plugin_runtime/src/clipboard.rs b/rust/plugin_runtime/src/clipboard.rs index 4f1b879..5cb917c 100644 --- a/rust/plugin_runtime/src/clipboard.rs +++ b/rust/plugin_runtime/src/clipboard.rs @@ -3,12 +3,12 @@ use std::rc::Rc; use deno_core::op2; use deno_core::OpState; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; +use gauntlet_common_plugin_runtime::model::JsClipboardData; -use crate::api::BackendForPluginRuntimeApi; -use crate::api::BackendForPluginRuntimeApiProxy; -use crate::DenoInClipboardData; -use crate::DenoOutClipboardData; -use crate::JsClipboardData; +use crate::model::DenoInClipboardData; +use crate::model::DenoOutClipboardData; #[op2(async)] #[serde] diff --git a/rust/plugin_runtime/src/deno.rs b/rust/plugin_runtime/src/deno.rs index ac73602..337ba72 100644 --- a/rust/plugin_runtime/src/deno.rs +++ b/rust/plugin_runtime/src/deno.rs @@ -29,12 +29,15 @@ use deno_runtime::worker::WorkerOptions; use deno_runtime::worker::WorkerServiceOptions; use deno_runtime::BootstrapOptions; use gauntlet_common::model::PluginId; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; +use gauntlet_common_plugin_runtime::model::JsEvent; +use gauntlet_common_plugin_runtime::model::JsInit; +use gauntlet_common_plugin_runtime::model::JsPluginCode; use once_cell::sync::Lazy; use regex::Regex; use tokio::runtime::Handle; use tokio::sync::mpsc::Receiver; -use crate::api::BackendForPluginRuntimeApiProxy; use crate::assets::asset_data; use crate::assets::asset_data_blocking; use crate::clipboard::clipboard_clear; @@ -50,13 +53,11 @@ use crate::environment::environment_plugin_cache_dir; use crate::environment::environment_plugin_data_dir; use crate::events::op_plugin_get_pending_event; use crate::events::EventReceiver; -use crate::events::JsEvent; use crate::logs::op_log_debug; use crate::logs::op_log_error; use crate::logs::op_log_info; use crate::logs::op_log_trace; use crate::logs::op_log_warn; -use crate::model::JsInit; use crate::permissions::permissions_to_deno; use crate::plugin_data::PluginData; use crate::plugins::applications::current_os; @@ -81,7 +82,6 @@ use crate::ui::show_hud; use crate::ui::show_plugin_error_view; use crate::ui::show_preferences_required_view; use crate::ui::update_loading_bar; -use crate::JsPluginCode; pub struct CustomModuleLoader { code: JsPluginCode, diff --git a/rust/plugin_runtime/src/entrypoint_generators.rs b/rust/plugin_runtime/src/entrypoint_generators.rs index d24db67..0117511 100644 --- a/rust/plugin_runtime/src/entrypoint_generators.rs +++ b/rust/plugin_runtime/src/entrypoint_generators.rs @@ -3,9 +3,8 @@ use std::rc::Rc; use deno_core::op2; use deno_core::OpState; - -use crate::api::BackendForPluginRuntimeApi; -use crate::api::BackendForPluginRuntimeApiProxy; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; #[op2(async)] #[serde] diff --git a/rust/plugin_runtime/src/events.rs b/rust/plugin_runtime/src/events.rs index c808a19..75b12d5 100644 --- a/rust/plugin_runtime/src/events.rs +++ b/rust/plugin_runtime/src/events.rs @@ -10,76 +10,11 @@ use deno_core::futures::StreamExt; use deno_core::op2; use deno_core::OpState; use gauntlet_common::model::UiWidgetId; +use gauntlet_common_plugin_runtime::model::JsEvent; use serde::Deserialize; use serde::Serialize; use tokio::sync::mpsc::Receiver; -use crate::api::BackendForPluginRuntimeApiProxy; -use crate::BackendForPluginRuntimeApi; - -#[derive(Debug, Deserialize, Serialize, Encode, Decode)] -#[serde(tag = "type")] -pub enum JsEvent { - OpenView { - #[serde(rename = "entrypointId")] - entrypoint_id: String, - }, - CloseView, - RunCommand { - #[serde(rename = "entrypointId")] - entrypoint_id: String, - }, - RunGeneratedEntrypoint { - #[serde(rename = "entrypointId")] - entrypoint_id: String, - #[serde(rename = "actionIndex")] - action_index: usize, - }, - ViewEvent { - #[serde(rename = "widgetId")] - widget_id: UiWidgetId, - #[serde(rename = "eventName")] - event_name: String, - #[serde(rename = "eventArguments")] - event_arguments: Vec, - }, - KeyboardEvent { - #[serde(rename = "entrypointId")] - entrypoint_id: String, - origin: JsKeyboardEventOrigin, - key: String, - #[serde(rename = "modifierShift")] - modifier_shift: bool, - #[serde(rename = "modifierControl")] - modifier_control: bool, - #[serde(rename = "modifierAlt")] - modifier_alt: bool, - #[serde(rename = "modifierMeta")] - modifier_meta: bool, - }, - OpenInlineView { - #[serde(rename = "text")] - text: String, - }, - RefreshSearchIndex, -} - -#[derive(Clone, Debug, Deserialize, Serialize, Encode, Decode)] -pub enum JsKeyboardEventOrigin { - MainView, - PluginView, -} - -// FIXME this could have been serde_v8::AnyValue but it doesn't support undefined, make a pr? -#[derive(Debug, Deserialize, Serialize, Encode, Decode)] -#[serde(tag = "type")] -pub enum JsUiPropertyValue { - String { value: String }, - Number { value: f64 }, - Bool { value: bool }, - Undefined, -} - pub struct EventReceiver { event_stream: Rc>>, } diff --git a/rust/plugin_runtime/src/lib.rs b/rust/plugin_runtime/src/lib.rs index 0a425e4..028409a 100644 --- a/rust/plugin_runtime/src/lib.rs +++ b/rust/plugin_runtime/src/lib.rs @@ -1,4 +1,3 @@ -mod api; mod assets; mod clipboard; mod component_model; @@ -28,13 +27,19 @@ use std::sync::Arc; use anyhow::anyhow; use anyhow::Context; -pub use api::BackendForPluginRuntimeApi; use bincode::Decode; use bincode::Encode; use deno_core::futures::SinkExt; -pub use events::JsEvent; -pub use events::JsKeyboardEventOrigin; -pub use events::JsUiPropertyValue; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiRequestData; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiResponseData; +use gauntlet_common_plugin_runtime::model::JsEvent; +use gauntlet_common_plugin_runtime::model::JsInit; +use gauntlet_common_plugin_runtime::model::JsMessage; +use gauntlet_common_plugin_runtime::model::JsPluginRuntimeMessage; +use gauntlet_common_plugin_runtime::recv_message; +use gauntlet_common_plugin_runtime::send_message; +use gauntlet_common_plugin_runtime::JsMessageSide; use gauntlet_utils::channel::Payload; use gauntlet_utils::channel::RequestReceiver; use interprocess::local_socket::tokio::prelude::*; @@ -44,9 +49,7 @@ use interprocess::local_socket::tokio::Stream; use interprocess::local_socket::GenericFilePath; use interprocess::local_socket::NameType; use interprocess::local_socket::ToNsName; -pub use model::*; use once_cell::sync::Lazy; -pub use permissions::PERMISSIONS_VARIABLE_PATTERN; use regex::Regex; use serde::de::DeserializeOwned; use serde::Deserialize; @@ -63,10 +66,6 @@ use tokio::sync::Mutex; use tokio::sync::MutexGuard; use tokio_util::sync::CancellationToken; -pub use crate::api::handle_proxy_message; -use crate::api::BackendForPluginRuntimeApiProxy; -use crate::api::BackendForPluginRuntimeApiRequestData; -use crate::api::BackendForPluginRuntimeApiResponseData; use crate::deno::start_js_runtime; pub fn run_plugin_runtime(socket_name: String) { @@ -310,66 +309,3 @@ async fn message_loop( } } } - -#[derive(Debug)] -pub enum JsMessageSide { - PluginRuntime, - Backend, -} - -static MESSAGE_ID: AtomicU32 = AtomicU32::new(0); - -pub async fn send_message(side: JsMessageSide, send: &mut SendHalf, value: T) -> anyhow::Result<()> { - let encoded: Vec = bincode::encode_to_vec(&value, bincode::config::standard())?; - - let message_id = MESSAGE_ID.fetch_add(1, Ordering::SeqCst); - - tracing::trace!( - side = debug(&side), - "Sending message with id {} and size of {} bytes: {:?}", - message_id, - encoded.len(), - &value - ); - - send.write_u32(message_id).await?; - - send.write_u32(encoded.len() as u32).await?; - - send.write_all(&encoded[..]).await?; - - tracing::trace!( - side = debug(&side), - "Message with id {} and size of {} bytes has been sent", - message_id, - encoded.len() - ); - - Ok(()) -} - -pub async fn recv_message(side: JsMessageSide, recv: &mut RecvHalf) -> anyhow::Result { - tracing::trace!(side = debug(&side), "Waiting for next message..."); - - let message_id = recv.read_u32().await?; - - tracing::trace!(side = debug(&side), "Reading message with id: {}", message_id); - - let buf_size = recv.read_u32().await?; - - let mut buffer = vec![0; buf_size as usize]; - - recv.read_exact(&mut buffer).await?; - - let (decoded, _) = bincode::decode_from_slice(&buffer[..], bincode::config::standard()) - .context(format!("Unable to deserialize message with id: {}", message_id))?; - - tracing::trace!( - side = debug(&side), - "Received message with id {}: {:?}", - message_id, - &decoded - ); - - Ok(decoded) -} diff --git a/rust/plugin_runtime/src/model.rs b/rust/plugin_runtime/src/model.rs index b6d02f0..4a21108 100644 --- a/rust/plugin_runtime/src/model.rs +++ b/rust/plugin_runtime/src/model.rs @@ -1,113 +1,10 @@ -use std::collections::HashMap; -use std::fmt; - -use bincode::Decode; -use bincode::Encode; use deno_core::JsBuffer; use deno_core::ToJsBuffer; -use gauntlet_common::model::EntrypointId; -use gauntlet_common::model::Icons; -use gauntlet_common::model::PluginId; +use gauntlet_common_plugin_runtime::model::JsGeneratedSearchItemAccessory; +use gauntlet_common_plugin_runtime::model::JsGeneratedSearchItemAction; use serde::Deserialize; use serde::Serialize; -use crate::api::BackendForPluginRuntimeApiRequestData; -use crate::api::BackendForPluginRuntimeApiResponseData; -use crate::JsEvent; - -#[derive(Debug, Encode, Decode)] -pub enum JsMessage { - Event(JsEvent), - Response(Result), - Stop, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize, Encode, Decode)] -pub enum JsUiRenderLocation { - InlineView, - View, -} - -#[derive(Debug, Encode, Decode)] -pub struct JsPluginCode { - pub js: HashMap, -} - -#[derive(Debug, Encode, Decode)] -pub struct JsInit { - pub plugin_id: PluginId, - pub plugin_uuid: String, - pub code: JsPluginCode, - pub permissions: JsPluginPermissions, - pub inline_view_entrypoint_id: Option, - pub entrypoint_names: HashMap, - pub dev_plugin: bool, - pub home_dir: String, - pub local_storage_dir: String, - pub plugin_cache_dir: String, - pub plugin_data_dir: String, - pub stdout_file: Option, - pub stderr_file: Option, -} - -#[derive(Debug, Encode, Decode)] -pub struct JsPluginPermissions { - pub environment: Vec, - pub network: Vec, - pub filesystem: JsPluginPermissionsFileSystem, - pub exec: JsPluginPermissionsExec, - pub system: Vec, - pub main_search_bar: Vec, -} - -#[derive(Debug, Encode, Decode)] -pub struct JsPluginPermissionsFileSystem { - pub read: Vec, - pub write: Vec, -} - -#[derive(Debug, Encode, Decode)] -pub struct JsPluginPermissionsExec { - pub command: Vec, - pub executable: Vec, -} - -#[derive(Clone, Debug, Encode, Decode)] -pub enum JsPluginPermissionsMainSearchBar { - Read, -} - -#[derive(Debug, Encode, Decode)] -pub enum JsPluginRuntimeMessage { - Stopped, - Request(BackendForPluginRuntimeApiRequestData), -} - -#[derive(Encode, Decode)] -pub struct JsGeneratedSearchItem { - pub entrypoint_name: String, - pub generator_entrypoint_id: String, - pub entrypoint_id: String, - pub entrypoint_uuid: String, - pub entrypoint_icon: Option>, - pub entrypoint_actions: Vec, - pub entrypoint_accessories: Vec, -} - -impl fmt::Debug for JsGeneratedSearchItem { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - // exclude entrypoint_icon - fmt.debug_struct("JsGeneratedSearchItem") - .field("entrypoint_name", &self.entrypoint_name) - .field("generator_entrypoint_id", &self.generator_entrypoint_id) - .field("entrypoint_id", &self.entrypoint_id) - .field("entrypoint_uuid", &self.entrypoint_uuid) - .field("entrypoint_actions", &self.entrypoint_actions) - .field("entrypoint_accessories", &self.entrypoint_accessories) - .finish() - } -} - #[derive(Serialize)] pub struct DenoOutGeneratedSearchItem { pub entrypoint_name: String, @@ -130,49 +27,6 @@ pub struct DenoInGeneratedSearchItem { pub entrypoint_accessories: Vec, } -#[derive(Debug, Deserialize, Serialize, Encode, Decode)] -pub struct JsGeneratedSearchItemAction { - pub id: Option, - pub action_type: JsGeneratedSearchItemActionType, - pub label: String, -} - -#[derive(Debug, Deserialize, Serialize, Encode, Decode)] -pub enum JsGeneratedSearchItemActionType { - View, - Command, -} - -#[derive(Debug, Deserialize, Serialize, Encode, Decode)] -#[serde(untagged)] -pub enum JsPreferenceUserData { - Number(f64), - String(String), - Bool(bool), - ListOfStrings(Vec), - ListOfNumbers(Vec), -} - -#[derive(Debug, Deserialize, Serialize, Encode, Decode)] -#[serde(untagged)] -pub enum JsGeneratedSearchItemAccessory { - TextAccessory { - text: String, - icon: Option, - tooltip: Option, - }, - IconAccessory { - icon: Icons, - tooltip: Option, - }, -} - -#[derive(Debug, Encode, Decode)] -pub struct JsClipboardData { - pub text_data: Option, - pub png_data: Option>, -} - #[derive(Serialize)] pub struct DenoOutClipboardData { pub text_data: Option, diff --git a/rust/plugin_runtime/src/permissions.rs b/rust/plugin_runtime/src/permissions.rs index cff27fc..fe66476 100644 --- a/rust/plugin_runtime/src/permissions.rs +++ b/rust/plugin_runtime/src/permissions.rs @@ -23,16 +23,13 @@ use deno_runtime::deno_permissions::UnaryPermission; use deno_runtime::deno_permissions::WriteDescriptor; use deno_runtime::permissions::RuntimePermissionDescriptorParser; use gauntlet_common::dirs::Dirs; +use gauntlet_common_plugin_runtime::model::JsPluginPermissions; +use gauntlet_common_plugin_runtime::model::JsPluginPermissionsExec; +use gauntlet_common_plugin_runtime::PERMISSIONS_VARIABLE_PATTERN; use once_cell::sync::Lazy; use regex::Regex; use typed_path::Utf8TypedPath; -use crate::JsPluginPermissions; -use crate::JsPluginPermissionsExec; - -pub static PERMISSIONS_VARIABLE_PATTERN: Lazy = - Lazy::new(|| Regex::new(r"\{(?.+?):(?.+?)}").expect("invalid regex")); - pub fn permissions_to_deno( fs: FileSystemRc, permissions: &JsPluginPermissions, diff --git a/rust/plugin_runtime/src/preferences.rs b/rust/plugin_runtime/src/preferences.rs index 770bac4..8aa2672 100644 --- a/rust/plugin_runtime/src/preferences.rs +++ b/rust/plugin_runtime/src/preferences.rs @@ -6,10 +6,9 @@ use deno_core::futures::executor::block_on; use deno_core::op2; use deno_core::OpState; use gauntlet_common::model::EntrypointId; - -use crate::api::BackendForPluginRuntimeApi; -use crate::api::BackendForPluginRuntimeApiProxy; -use crate::model::JsPreferenceUserData; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; +use gauntlet_common_plugin_runtime::model::JsPreferenceUserData; #[op2] #[serde] diff --git a/rust/plugin_runtime/src/search.rs b/rust/plugin_runtime/src/search.rs index 0c999f8..6f8d784 100644 --- a/rust/plugin_runtime/src/search.rs +++ b/rust/plugin_runtime/src/search.rs @@ -3,11 +3,11 @@ use std::rc::Rc; use deno_core::op2; use deno_core::OpState; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; +use gauntlet_common_plugin_runtime::model::JsGeneratedSearchItem; -use crate::api::BackendForPluginRuntimeApi; -use crate::api::BackendForPluginRuntimeApiProxy; -use crate::DenoInGeneratedSearchItem; -use crate::JsGeneratedSearchItem; +use crate::model::DenoInGeneratedSearchItem; #[op2(async)] pub async fn reload_search_index( diff --git a/rust/plugin_runtime/src/ui.rs b/rust/plugin_runtime/src/ui.rs index 01581b4..3c1d7c3 100644 --- a/rust/plugin_runtime/src/ui.rs +++ b/rust/plugin_runtime/src/ui.rs @@ -78,6 +78,9 @@ use gauntlet_common::model::UiPropertyValue; use gauntlet_common::model::UiRenderLocation; use gauntlet_common::model::UiWidgetId; use gauntlet_common::model::WidgetVisitor; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; +use gauntlet_common_plugin_runtime::model::JsUiRenderLocation; use gauntlet_component_model::Component; use gauntlet_component_model::Component::Root; use gauntlet_component_model::Property; @@ -91,10 +94,7 @@ use serde::Deserializer; use serde::Serialize; use tokio::runtime::Handle; -use crate::api::BackendForPluginRuntimeApi; -use crate::api::BackendForPluginRuntimeApiProxy; use crate::component_model::ComponentModel; -use crate::model::JsUiRenderLocation; use crate::plugin_data::PluginData; #[op2] diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index fb0b56e..26681ce 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -7,7 +7,7 @@ edition.workspace = true gauntlet-common.workspace = true gauntlet-utils.workspace = true gauntlet-client.workspace = true -gauntlet-plugin-runtime.workspace = true +gauntlet-common-plugin-runtime.workspace = true gauntlet-scenario-runner = { workspace = true, optional = true } # shared @@ -44,8 +44,8 @@ dark-light = "1.1.1" schemars = "0.8" [features] -release = ["gauntlet-common/release", "gauntlet-plugin-runtime/release"] -scenario_runner = ["dep:gauntlet-scenario-runner", "gauntlet-common/scenario_runner", "gauntlet-plugin-runtime/scenario_runner"] +release = ["gauntlet-common/release"] +scenario_runner = ["dep:gauntlet-scenario-runner", "gauntlet-common/scenario_runner"] [build-dependencies] vergen-gitcl = { version = "1.0", features = ["build", "cargo"] } diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index db20d80..690a27a 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -27,7 +27,6 @@ use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; use gauntlet_common::settings_env_data_from_string; use gauntlet_common::settings_env_data_to_string; use gauntlet_common::SettingsEnvData; -use gauntlet_plugin_runtime::run_plugin_runtime; use gauntlet_utils::channel::channel; use gauntlet_utils::channel::RequestError; use gauntlet_utils::channel::RequestReceiver; @@ -50,7 +49,8 @@ pub fn start(minimized: bool) { register_panic_hook(std::env::var(PLUGIN_UUID_ENV).ok()); if let Ok(socket_name) = std::env::var(PLUGIN_CONNECT_ENV) { - run_plugin_runtime(socket_name); + // this is not actually unresolved item, see .cargo/config.toml + gauntlet_plugin_runtime::run_plugin_runtime(socket_name); return; } diff --git a/rust/server/src/plugins/binary_data_gatherer.rs b/rust/server/src/plugins/binary_data_gatherer.rs index 535d842..2c4ee02 100644 --- a/rust/server/src/plugins/binary_data_gatherer.rs +++ b/rust/server/src/plugins/binary_data_gatherer.rs @@ -10,7 +10,7 @@ use gauntlet_common::model::RootWidget; use gauntlet_common::model::SvgWidget; use gauntlet_common::model::UiWidgetId; use gauntlet_common::model::WidgetVisitor; -use gauntlet_plugin_runtime::BackendForPluginRuntimeApi; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use crate::plugins::js::BackendForPluginRuntimeApiImpl; diff --git a/rust/server/src/plugins/clipboard.rs b/rust/server/src/plugins/clipboard.rs index 74a6823..3781dbe 100644 --- a/rust/server/src/plugins/clipboard.rs +++ b/rust/server/src/plugins/clipboard.rs @@ -6,7 +6,7 @@ use anyhow::anyhow; use anyhow::Context; use anyhow::Error; use arboard::ImageData; -use gauntlet_plugin_runtime::JsClipboardData; +use gauntlet_common_plugin_runtime::model::JsClipboardData; use image::RgbaImage; #[derive(Clone)] diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index d02418f..d46c970 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -29,28 +29,28 @@ use gauntlet_common::model::UiWidgetId; use gauntlet_common::rpc::frontend_api::FrontendApi; use gauntlet_common::rpc::frontend_api::FrontendApiProxy; use gauntlet_common::settings_env_data_to_string; -use gauntlet_plugin_runtime::handle_proxy_message; -use gauntlet_plugin_runtime::recv_message; -use gauntlet_plugin_runtime::send_message; -use gauntlet_plugin_runtime::BackendForPluginRuntimeApi; -use gauntlet_plugin_runtime::JsClipboardData; -use gauntlet_plugin_runtime::JsEvent; -use gauntlet_plugin_runtime::JsGeneratedSearchItem; -use gauntlet_plugin_runtime::JsGeneratedSearchItemAccessory; -use gauntlet_plugin_runtime::JsGeneratedSearchItemActionType; -use gauntlet_plugin_runtime::JsInit; -use gauntlet_plugin_runtime::JsKeyboardEventOrigin; -use gauntlet_plugin_runtime::JsMessage; -use gauntlet_plugin_runtime::JsMessageSide; -use gauntlet_plugin_runtime::JsPluginCode; -use gauntlet_plugin_runtime::JsPluginPermissions; -use gauntlet_plugin_runtime::JsPluginPermissionsExec; -use gauntlet_plugin_runtime::JsPluginPermissionsFileSystem; -use gauntlet_plugin_runtime::JsPluginPermissionsMainSearchBar; -use gauntlet_plugin_runtime::JsPluginRuntimeMessage; -use gauntlet_plugin_runtime::JsPreferenceUserData; -use gauntlet_plugin_runtime::JsUiPropertyValue; -use gauntlet_plugin_runtime::JsUiRenderLocation; +use gauntlet_common_plugin_runtime::api::handle_proxy_message; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; +use gauntlet_common_plugin_runtime::model::JsClipboardData; +use gauntlet_common_plugin_runtime::model::JsEvent; +use gauntlet_common_plugin_runtime::model::JsGeneratedSearchItem; +use gauntlet_common_plugin_runtime::model::JsGeneratedSearchItemAccessory; +use gauntlet_common_plugin_runtime::model::JsGeneratedSearchItemActionType; +use gauntlet_common_plugin_runtime::model::JsInit; +use gauntlet_common_plugin_runtime::model::JsKeyboardEventOrigin; +use gauntlet_common_plugin_runtime::model::JsMessage; +use gauntlet_common_plugin_runtime::model::JsPluginCode; +use gauntlet_common_plugin_runtime::model::JsPluginPermissions; +use gauntlet_common_plugin_runtime::model::JsPluginPermissionsExec; +use gauntlet_common_plugin_runtime::model::JsPluginPermissionsFileSystem; +use gauntlet_common_plugin_runtime::model::JsPluginPermissionsMainSearchBar; +use gauntlet_common_plugin_runtime::model::JsPluginRuntimeMessage; +use gauntlet_common_plugin_runtime::model::JsPreferenceUserData; +use gauntlet_common_plugin_runtime::model::JsUiPropertyValue; +use gauntlet_common_plugin_runtime::model::JsUiRenderLocation; +use gauntlet_common_plugin_runtime::recv_message; +use gauntlet_common_plugin_runtime::send_message; +use gauntlet_common_plugin_runtime::JsMessageSide; use gauntlet_utils::channel::RequestResult; use interprocess::local_socket::tokio::RecvHalf; use interprocess::local_socket::tokio::SendHalf; diff --git a/rust/server/src/plugins/loader.rs b/rust/server/src/plugins/loader.rs index 1da2a02..61dfa0f 100644 --- a/rust/server/src/plugins/loader.rs +++ b/rust/server/src/plugins/loader.rs @@ -10,7 +10,7 @@ use anyhow::anyhow; use anyhow::Context; use gauntlet_common::model::DownloadStatus; use gauntlet_common::model::PluginId; -use gauntlet_plugin_runtime::PERMISSIONS_VARIABLE_PATTERN; +use gauntlet_common_plugin_runtime::PERMISSIONS_VARIABLE_PATTERN; use include_dir::Dir; use itertools::Itertools; use once_cell::sync::Lazy; diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index c015e99..11f1a8c 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -38,11 +38,10 @@ use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; use gauntlet_common::settings_env_data_to_string; use gauntlet_common::SettingsEnvData; use gauntlet_common::SETTINGS_ENV; -use gauntlet_plugin_runtime::JsPluginCode; -use gauntlet_plugin_runtime::JsPluginPermissions; -use gauntlet_plugin_runtime::JsPluginPermissionsExec; -use gauntlet_plugin_runtime::JsPluginPermissionsFileSystem; -use gauntlet_plugin_runtime::JsPluginPermissionsMainSearchBar; +use gauntlet_common_plugin_runtime::model::JsPluginCode; +use gauntlet_common_plugin_runtime::model::JsPluginPermissionsExec; +use gauntlet_common_plugin_runtime::model::JsPluginPermissionsFileSystem; +use gauntlet_common_plugin_runtime::model::JsPluginPermissionsMainSearchBar; use gauntlet_utils::channel::RequestResult; use gauntlet_utils::channel::RequestSender; use include_dir::include_dir; From a8182f4b2ef5a79ac22e8e5d257503c9ba2a2f90 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 24 May 2025 10:08:44 +0200 Subject: [PATCH 05/91] Switch to edition 2024, resolve all cargo warnings --- Cargo.toml | 2 +- rust/cli/src/lib.rs | 8 +- rust/client/build.rs | 3 +- rust/client/src/lib.rs | 3 - rust/client/src/ui/client_context.rs | 1 + rust/client/src/ui/grid_navigation.rs | 2 +- rust/client/src/ui/mod.rs | 136 +++++------------- rust/client/src/ui/scroll_handle.rs | 2 - rust/client/src/ui/search_list.rs | 3 - rust/client/src/ui/state/mod.rs | 4 +- rust/client/src/ui/theme/button.rs | 1 + rust/client/src/ui/theme/checkbox.rs | 5 +- rust/client/src/ui/theme/row.rs | 1 - rust/client/src/ui/widget/data.rs | 3 - rust/client/src/ui/widget/data_mut.rs | 4 +- rust/client/src/ui/widget_container.rs | 35 ++--- rust/common/src/dirs.rs | 1 - rust/common/src/rpc/frontend_api.rs | 10 +- rust/common_plugin_runtime/src/lib.rs | 18 --- rust/common_ui/src/lib.rs | 1 - rust/component_model/src/lib.rs | 1 + .../src/components/shortcut_selector.rs | 10 +- rust/management_client/src/theme.rs | 3 + rust/management_client/src/theme/button.rs | 1 + rust/management_client/src/theme/container.rs | 13 -- rust/management_client/src/ui.rs | 20 +-- rust/management_client/src/views/general.rs | 9 +- rust/management_client/src/views/plugins.rs | 18 +-- .../src/views/plugins/preferences.rs | 2 +- .../src/views/plugins/table.rs | 3 +- rust/plugin_runtime/Cargo.toml | 2 +- rust/plugin_runtime/src/deno.rs | 18 ++- rust/plugin_runtime/src/events.rs | 10 +- rust/plugin_runtime/src/lib.rs | 42 ++---- rust/plugin_runtime/src/permissions.rs | 8 +- rust/plugin_runtime/src/plugin_data.rs | 1 + .../src/plugins/applications.rs | 17 +-- .../src/plugins/applications/linux/mod.rs | 13 +- .../applications/linux/wayland/cosmic.rs | 15 +- .../plugins/applications/linux/wayland/mod.rs | 20 ++- .../plugins/applications/linux/wayland/wlr.rs | 15 +- .../src/plugins/applications/linux/x11.rs | 24 ++-- rust/plugin_runtime/src/ui.rs | 80 +---------- rust/server/src/lib.rs | 9 +- .../src/plugins/binary_data_gatherer.rs | 1 - rust/server/src/plugins/clipboard.rs | 2 +- rust/server/src/plugins/config_reader.rs | 30 +--- rust/server/src/plugins/data_db_repository.rs | 11 +- rust/server/src/plugins/frecency.rs | 4 + rust/server/src/plugins/js.rs | 31 +--- rust/server/src/plugins/loader.rs | 11 -- rust/server/src/plugins/mod.rs | 53 ++----- rust/server/src/plugins/run_status.rs | 2 +- rust/server/src/plugins/settings.rs | 8 +- rust/server/src/plugins/theme.rs | 6 - rust/server/src/rpc.rs | 8 -- rust/server/src/search.rs | 6 +- rust/utils/src/channel.rs | 2 +- 58 files changed, 184 insertions(+), 588 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2af9ca7..77705c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ members = [ ] [workspace.package] -edition = "2021" +edition = "2024" [workspace.dependencies] # iced diff --git a/rust/cli/src/lib.rs b/rust/cli/src/lib.rs index 74b8934..888f0aa 100644 --- a/rust/cli/src/lib.rs +++ b/rust/cli/src/lib.rs @@ -1,7 +1,3 @@ -use std::time::Duration; - -use anyhow::anyhow; -use anyhow::Context; use clap::Parser; use gauntlet_client::open_window; use gauntlet_management_client::start_management_client; @@ -99,6 +95,8 @@ pub fn init() { #[cfg(target_os = "macos")] fn setup_auto_launch_macos() -> anyhow::Result<()> { + use anyhow::anyhow; + use anyhow::Context; let app_path = std::env::current_exe().context("Unable to get current_exe from env")?; // expect Gauntlet.app in path according to macos app bundle structure @@ -119,6 +117,8 @@ fn setup_auto_launch_macos() -> anyhow::Result<()> { #[cfg(target_os = "windows")] fn setup_auto_launch_windows() -> anyhow::Result<()> { + use anyhow::anyhow; + use anyhow::Context; let app_path = std::env::current_exe() .context("Unable to get current_exe from env")? .as_os_str() diff --git a/rust/client/build.rs b/rust/client/build.rs index 7acf5a9..2657559 100644 --- a/rust/client/build.rs +++ b/rust/client/build.rs @@ -35,6 +35,7 @@ fn main() -> anyhow::Result<()> { output.push_str(" widget_id: UiWidgetId,\n"); for arg in arguments { + output.push_str(&" #[allow(non_snake_case)]\n".to_string()); output.push_str(&format!(" {}: {}\n", arg.name, generate_type(&arg, name))); } @@ -48,7 +49,7 @@ fn main() -> anyhow::Result<()> { match arg.property_type { PropertyType::String => { if arg.optional { - output.push_str(&format!(" {}.map(|{}| gauntlet_common::model::UiPropertyValue::String({})).unwrap_or_else(|| gauntlet_common::model::UiPropertyValue::Undefined),\n", arg.name, arg.name, arg.name)); + output.push_str(&format!(" {}.map(|#[allow(non_snake_case)] {}| gauntlet_common::model::UiPropertyValue::String({})).unwrap_or_else(|| gauntlet_common::model::UiPropertyValue::Undefined),\n", arg.name, arg.name, arg.name)); } else { output.push_str(&format!( " gauntlet_common::model::UiPropertyValue::String({}),\n", diff --git a/rust/client/src/lib.rs b/rust/client/src/lib.rs index 550be8f..905dbcd 100644 --- a/rust/client/src/lib.rs +++ b/rust/client/src/lib.rs @@ -1,4 +1,3 @@ -use gauntlet_common::dirs::Dirs; use gauntlet_common::rpc::backend_api::BackendForCliApi; use gauntlet_common::rpc::backend_api::BackendForCliApiProxy; use gauntlet_common::rpc::backend_api::BackendForFrontendApiRequestData; @@ -9,8 +8,6 @@ use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; use gauntlet_utils::channel::RequestReceiver; use gauntlet_utils::channel::RequestSender; -use crate::ui::GauntletComplexTheme; - pub mod global_shortcut; pub(crate) mod model; pub(crate) mod ui; diff --git a/rust/client/src/ui/client_context.rs b/rust/client/src/ui/client_context.rs index a94edab..2979d45 100644 --- a/rust/client/src/ui/client_context.rs +++ b/rust/client/src/ui/client_context.rs @@ -49,6 +49,7 @@ impl ClientContext { .flatten() } + #[allow(unused)] pub fn get_inline_view_container(&self, plugin_id: &PluginId) -> &PluginWidgetContainer { self.inline_views .iter() diff --git a/rust/client/src/ui/grid_navigation.rs b/rust/client/src/ui/grid_navigation.rs index 12ccf4b..e868160 100644 --- a/rust/client/src/ui/grid_navigation.rs +++ b/rust/client/src/ui/grid_navigation.rs @@ -29,7 +29,7 @@ pub struct GridItemOffset { } fn grid_row_data( - mut amount_per_section_total: Vec, + amount_per_section_total: Vec, current_index: usize, ) -> (Option, GridCurrentRowData, Option) { let mut previous_section_index: Option = None; diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 13e1cc8..524aed1 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -2,11 +2,8 @@ use std::collections::HashMap; use std::fs; use std::path::Path; use std::path::PathBuf; -use std::rc::Rc; use std::sync::Arc; -use std::sync::Mutex as StdMutex; use std::sync::Mutex; -use std::sync::RwLock as StdRwLock; use anyhow::anyhow; use client_context::ClientContext; @@ -16,13 +13,10 @@ use gauntlet_common::model::PhysicalKey; use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; use gauntlet_common::model::RootWidget; -use gauntlet_common::model::RootWidgetMembers; use gauntlet_common::model::SearchResult; -use gauntlet_common::model::SearchResultEntrypointAction; use gauntlet_common::model::SearchResultEntrypointActionType; use gauntlet_common::model::SearchResultEntrypointType; use gauntlet_common::model::UiRenderLocation; -use gauntlet_common::model::UiSetupData; use gauntlet_common::model::UiTheme; use gauntlet_common::model::UiWidgetId; use gauntlet_common::model::WindowPositionMode; @@ -30,13 +24,10 @@ use gauntlet_common::rpc::backend_api::BackendForFrontendApi; use gauntlet_common::rpc::backend_api::BackendForFrontendApiProxy; use gauntlet_common::rpc::backend_api::BackendForFrontendApiRequestData; use gauntlet_common::rpc::backend_api::BackendForFrontendApiResponseData; -use gauntlet_common::rpc::backend_api::GrpcBackendApi; -use gauntlet_common::rpc::frontend_api::handle_proxy_message; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; use gauntlet_common::scenario_convert::ui_render_location_from_scenario; use gauntlet_common::scenario_model::ScenarioFrontendEvent; -use gauntlet_common::scenario_model::ScenarioUiRenderLocation; use gauntlet_common_ui::physical_key_model; use gauntlet_utils::channel::RequestError; use gauntlet_utils::channel::RequestReceiver; @@ -46,20 +37,16 @@ use gauntlet_utils::channel::Responder; use global_hotkey::hotkey::HotKey; use global_hotkey::GlobalHotKeyManager; use iced::advanced::graphics::core::SmolStr; -use iced::advanced::layout::Limits; use iced::alignment::Horizontal; use iced::alignment::Vertical; use iced::event; -use iced::executor; use iced::font; use iced::futures; use iced::futures::SinkExt; use iced::keyboard; -use iced::keyboard::key; use iced::keyboard::key::Named; use iced::keyboard::key::Physical; use iced::keyboard::Key; -use iced::keyboard::Location; use iced::keyboard::Modifiers; use iced::stream; use iced::widget::button; @@ -67,26 +54,18 @@ use iced::widget::column; use iced::widget::container; use iced::widget::horizontal_rule; use iced::widget::horizontal_space; -use iced::widget::row; use iced::widget::scrollable; -use iced::widget::scrollable::scroll_to; -use iced::widget::scrollable::AbsoluteOffset; use iced::widget::text; use iced::widget::text::Shaping; use iced::widget::text_input; use iced::widget::text_input::focus; -use iced::widget::Space; use iced::window; use iced::window::Level; use iced::window::Mode; use iced::window::Position; use iced::window::Screenshot; -use iced::Alignment; use iced::Event; -use iced::Font; use iced::Length; -use iced::Padding; -use iced::Pixels; use iced::Point; use iced::Renderer; use iced::Settings; @@ -94,11 +73,7 @@ use iced::Size; use iced::Subscription; use iced::Task; use iced_fonts::BOOTSTRAP_FONT_BYTES; -use serde::Deserialize; -use serde_json::map::Entry; use tokio::runtime::Handle; -use tokio::sync::oneshot; -use tokio::sync::Mutex as TokioMutex; use tokio::sync::RwLock as TokioRwLock; use crate::model::UiViewEvent; @@ -144,7 +119,6 @@ use crate::ui::widget::action_panel::ActionPanel; use crate::ui::widget::action_panel::ActionPanelItem; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::root::render_root; -use crate::ui::widget_container::PluginWidgetContainer; pub struct AppModel { // logic @@ -189,28 +163,20 @@ mod layer_shell { pub enum AppMsg { OpenView { plugin_id: PluginId, - plugin_name: String, entrypoint_id: EntrypointId, - entrypoint_name: String, }, OpenGeneratedView { plugin_id: PluginId, - plugin_name: String, entrypoint_id: EntrypointId, - entrypoint_name: String, action_index: usize, }, ShowNewView { plugin_id: PluginId, - plugin_name: String, entrypoint_id: EntrypointId, - entrypoint_name: String, }, ShowNewGeneratedView { plugin_id: PluginId, - plugin_name: String, entrypoint_id: EntrypointId, - entrypoint_name: String, action_index: usize, }, RunCommand { @@ -563,7 +529,7 @@ fn new( wayland: bool, minimized: bool, ) -> (AppModel, Task) { - let mut backend_api = BackendForFrontendApiProxy::new(backend_sender); + let backend_api = BackendForFrontendApiProxy::new(backend_sender); let setup_data = futures::executor::block_on(backend_api.setup_data()).expect("Unable to setup frontend"); @@ -686,9 +652,7 @@ fn new( PluginViewData { top_level_view, plugin_id, - plugin_name, entrypoint_id, - entrypoint_name, action_shortcuts: Default::default(), }, true, @@ -774,9 +738,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { match message { AppMsg::OpenView { plugin_id, - plugin_name, entrypoint_id, - entrypoint_name, } => { match &mut state.global_state { GlobalState::MainView { @@ -786,9 +748,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { *pending_plugin_view_data = Some(PluginViewData { top_level_view: true, plugin_id: plugin_id.clone(), - plugin_name, entrypoint_id: entrypoint_id.clone(), - entrypoint_name, action_shortcuts: HashMap::new(), }); @@ -804,9 +764,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } AppMsg::OpenGeneratedView { plugin_id, - plugin_name, entrypoint_id, - entrypoint_name, action_index, } => { match &mut state.global_state { @@ -817,9 +775,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { *pending_plugin_view_data = Some(PluginViewData { top_level_view: true, plugin_id: plugin_id.clone(), - plugin_name, entrypoint_id: entrypoint_id.clone(), - entrypoint_name, action_shortcuts: HashMap::new(), }); @@ -891,9 +847,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { if action_index == 0 { Task::done(AppMsg::OpenView { plugin_id: search_result.plugin_id.clone(), - plugin_name: search_result.plugin_name.clone(), entrypoint_id: search_result.entrypoint_id.clone(), - entrypoint_name: search_result.entrypoint_name.clone(), }) } else { Task::none() @@ -912,9 +866,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { SearchResultEntrypointActionType::View => { Task::done(AppMsg::OpenGeneratedView { plugin_id: search_result.plugin_id.clone(), - plugin_name: search_result.plugin_name.clone(), entrypoint_id: search_result.entrypoint_id.clone(), - entrypoint_name: action.label.clone(), action_index, }) } @@ -1507,7 +1459,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } AppMsg::SetGlobalShortcut { shortcut, responder } => { - let mut responder = responder + let responder = responder .lock() .expect("lock is poisoned") .take() @@ -1540,7 +1492,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { shortcut, responder, } => { - let mut responder = responder + let responder = responder .lock() .expect("lock is poisoned") .take() @@ -1657,9 +1609,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } AppMsg::ShowNewView { plugin_id, - plugin_name, entrypoint_id, - entrypoint_name, } => { Task::batch([ GlobalState::pending_plugin( @@ -1667,9 +1617,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { PluginViewData { top_level_view: true, plugin_id: plugin_id.clone(), - plugin_name, entrypoint_id: entrypoint_id.clone(), - entrypoint_name, action_shortcuts: HashMap::new(), }, ), @@ -1679,9 +1627,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } AppMsg::ShowNewGeneratedView { plugin_id, - plugin_name, entrypoint_id, - entrypoint_name, action_index, } => { Task::batch([ @@ -1690,9 +1636,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { PluginViewData { top_level_view: true, plugin_id: plugin_id.clone(), - plugin_name, entrypoint_id: entrypoint_id.clone(), - entrypoint_name, action_shortcuts: HashMap::new(), }, ), @@ -1743,7 +1687,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { plugin_id, entrypoint_id, } => { - let mut backend_client = state.backend_api.clone(); + let backend_client = state.backend_api.clone(); Task::perform( async move { @@ -1751,7 +1695,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Ok(result) }, - |result| handle_backend_error(result, |action_shortcuts| AppMsg::Noop), + |result| handle_backend_error(result, |()| AppMsg::Noop), ) } } @@ -2021,15 +1965,13 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { let inline_view = match state.client_context.get_all_inline_view_containers().first() { Some((plugin_id, container)) => { let plugin_id = plugin_id.clone(); - container - .render_inline_root_widget(plugin_id.clone()) - .map(move |widget_event| { - AppMsg::WidgetEvent { - plugin_id: plugin_id.clone(), - render_location: UiRenderLocation::InlineView, - widget_event, - } - }) + container.render_inline_root_widget().map(move |widget_event| { + AppMsg::WidgetEvent { + plugin_id: plugin_id.clone(), + render_location: UiRenderLocation::InlineView, + widget_event, + } + }) } None => horizontal_space().into(), }; @@ -2113,7 +2055,7 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { .map(|action| action.label.clone()) .unwrap_or_else(|| label.to_string()); // should never happen, because there is always at least one action - let mut actions: Vec<_> = search_item + let actions: Vec<_> = search_item .entrypoint_actions .iter() .enumerate() @@ -2198,7 +2140,7 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { "", || AppMsg::ToggleActionPanel { keyboard: false }, |widget_id| AppMsg::OnPrimaryActionMainViewActionPanelMouse { widget_id }, - |widget_id| AppMsg::Noop, + |_widget_id| AppMsg::Noop, || AppMsg::Noop, ) } @@ -2262,15 +2204,16 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { let view_container = state.client_context.get_view_container(); - let container_element = view_container - .render_root_widget(plugin_id.clone(), sub_state, action_shortcuts) - .map(|widget_event| { - AppMsg::WidgetEvent { - plugin_id: plugin_id.clone(), - render_location: UiRenderLocation::View, - widget_event, - } - }); + let container_element = + view_container + .render_root_widget(sub_state, action_shortcuts) + .map(|widget_event| { + AppMsg::WidgetEvent { + plugin_id: plugin_id.clone(), + render_location: UiRenderLocation::View, + widget_event, + } + }); let element: Element<_> = container(container_element) .width(Length::Fill) @@ -2395,6 +2338,7 @@ impl AppModel { Task::none() } + #[allow(unused)] fn on_unfocused(&mut self) -> Task { // for some reason (on both macOS and linux x11 but x11 now uses separate impl) duplicate Unfocused fires right before Focus event if self.focused { @@ -2513,7 +2457,7 @@ impl AppModel { } fn open_plugin_view(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> Task { - let mut backend_client = self.backend_api.clone(); + let backend_client = self.backend_api.clone(); Task::perform( async move { @@ -2526,7 +2470,7 @@ impl AppModel { } fn close_plugin_view(&self, plugin_id: PluginId) -> Task { - let mut backend_client = self.backend_api.clone(); + let backend_client = self.backend_api.clone(); Task::perform( async move { @@ -2539,7 +2483,7 @@ impl AppModel { } fn run_command(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> Task { - let mut backend_client = self.backend_api.clone(); + let backend_client = self.backend_api.clone(); Task::perform( async move { @@ -2557,7 +2501,7 @@ impl AppModel { entrypoint_id: EntrypointId, action_index: usize, ) -> Task { - let mut backend_client = self.backend_api.clone(); + let backend_client = self.backend_api.clone(); Task::perform( async move { @@ -2577,7 +2521,7 @@ impl AppModel { plugin_id: PluginId, render_location: UiRenderLocation, ) -> Task { - let mut backend_client = self.backend_api.clone(); + let backend_client = self.backend_api.clone(); let event = self .client_context @@ -2633,7 +2577,7 @@ impl AppModel { modifier_alt: bool, modifier_meta: bool, ) -> Task { - let mut backend_client = self.backend_api.clone(); + let backend_client = self.backend_api.clone(); Task::perform( async move { @@ -2664,7 +2608,7 @@ impl AppModel { modifier_alt: bool, modifier_meta: bool, ) -> Task { - let mut backend_client = self.backend_api.clone(); + let backend_client = self.backend_api.clone(); let (plugin_id, entrypoint_id) = { ( @@ -2702,7 +2646,7 @@ impl AppModel { modifier_alt: bool, modifier_meta: bool, ) -> Task { - let mut backend_client = self.backend_api.clone(); + let backend_client = self.backend_api.clone(); let (plugin_id, entrypoint_id) = { match self.client_context.get_first_inline_view_container() { @@ -2733,7 +2677,7 @@ impl AppModel { } fn search(&self, new_prompt: String, render_inline_view: bool) -> Task { - let mut backend_api = self.backend_api.clone(); + let backend_api = self.backend_api.clone(); Task::perform( async move { @@ -2750,7 +2694,7 @@ impl AppModel { plugin_id: PluginId, entrypoint_id: Option, ) -> Task { - let mut backend_api = self.backend_api.clone(); + let backend_api = self.backend_api.clone(); Task::perform( async move { @@ -2765,7 +2709,7 @@ impl AppModel { } fn inline_view_shortcuts(&self) -> Task { - let mut backend_api = self.backend_api.clone(); + let backend_api = self.backend_api.clone(); Task::perform(async move { backend_api.inline_view_shortcuts().await }, |result| { handle_backend_error(result, |shortcuts| AppMsg::InlineViewShortcuts { shortcuts }) @@ -3135,33 +3079,25 @@ async fn request_loop( } FrontendApiRequestData::OpenPluginView { plugin_id, - plugin_name, entrypoint_id, - entrypoint_name, } => { responder.respond(Ok(FrontendApiResponseData::OpenPluginView { data: () })); AppMsg::ShowNewView { plugin_id, - plugin_name, entrypoint_id, - entrypoint_name, } } FrontendApiRequestData::OpenGeneratedPluginView { plugin_id, - plugin_name, entrypoint_id, - entrypoint_name, action_index, } => { responder.respond(Ok(FrontendApiResponseData::OpenGeneratedPluginView { data: () })); AppMsg::ShowNewGeneratedView { plugin_id, - plugin_name, entrypoint_id, - entrypoint_name, action_index, } } diff --git a/rust/client/src/ui/scroll_handle.rs b/rust/client/src/ui/scroll_handle.rs index 9c4d1a8..0e83688 100644 --- a/rust/client/src/ui/scroll_handle.rs +++ b/rust/client/src/ui/scroll_handle.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use iced::widget::scrollable::scroll_to; use iced::widget::scrollable::AbsoluteOffset; use iced::widget::scrollable::Id; diff --git a/rust/client/src/ui/search_list.rs b/rust/client/src/ui/search_list.rs index c8a5533..dbc448a 100644 --- a/rust/client/src/ui/search_list.rs +++ b/rust/client/src/ui/search_list.rs @@ -7,7 +7,6 @@ use gauntlet_common::model::SearchResultAccessory; use gauntlet_common::model::SearchResultEntrypointType; use gauntlet_common::model::TextAccessoryWidget; use iced::advanced::image::Handle; -use iced::color; use iced::widget::button; use iced::widget::column; use iced::widget::container; @@ -15,9 +14,7 @@ use iced::widget::horizontal_space; use iced::widget::row; use iced::widget::text; use iced::widget::text::Shaping; -use iced::widget::text_input; use iced::Alignment; -use iced::Font; use iced::Length; use crate::ui::scroll_handle::ScrollHandle; diff --git a/rust/client/src/ui/state/mod.rs b/rust/client/src/ui/state/mod.rs index fade508..21a94ee 100644 --- a/rust/client/src/ui/state/mod.rs +++ b/rust/client/src/ui/state/mod.rs @@ -49,9 +49,7 @@ pub enum GlobalState { pub struct PluginViewData { pub top_level_view: bool, pub plugin_id: PluginId, - pub plugin_name: String, pub entrypoint_id: EntrypointId, - pub entrypoint_name: String, pub action_shortcuts: HashMap, } @@ -63,7 +61,9 @@ pub enum ErrorViewData { entrypoint_preferences_required: bool, }, PluginError { + #[allow(unused)] plugin_id: PluginId, + #[allow(unused)] entrypoint_id: EntrypointId, }, BackendTimeout, diff --git a/rust/client/src/ui/theme/button.rs b/rust/client/src/ui/theme/button.rs index 232bb19..e00d6ae 100644 --- a/rust/client/src/ui/theme/button.rs +++ b/rust/client/src/ui/theme/button.rs @@ -16,6 +16,7 @@ use crate::ui::theme::NOT_INTENDED_TO_BE_USED; #[derive(Debug, Clone, Copy)] pub enum ButtonStyle { + #[allow(unused)] ShouldNotBeUsed, DatePicker, diff --git a/rust/client/src/ui/theme/checkbox.rs b/rust/client/src/ui/theme/checkbox.rs index 861e7a2..3d9f861 100644 --- a/rust/client/src/ui/theme/checkbox.rs +++ b/rust/client/src/ui/theme/checkbox.rs @@ -5,7 +5,6 @@ use iced::widget::Checkbox; use iced::Border; use iced::Renderer; -use crate::ui::theme::get_theme; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; use crate::ui::theme::ThemableWidget; @@ -22,7 +21,7 @@ impl checkbox::Catalog for GauntletComplexTheme { CheckboxStyle::Default } - fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { + fn style(&self, _class: &Self::Class<'_>, status: Status) -> Style { match status { Status::Active { is_checked } => active(self, is_checked), Status::Hovered { is_checked } => hovered(self, is_checked), @@ -90,8 +89,6 @@ impl<'a, Message: 'a> ThemableWidget<'a, Message> for Checkbox<'a, Message, Gaun type Kind = CheckboxStyle; fn themed(self, style: CheckboxStyle) -> Element<'a, Message> { - let theme = get_theme(); - match style { CheckboxStyle::Default => { self.class(style) diff --git a/rust/client/src/ui/theme/row.rs b/rust/client/src/ui/theme/row.rs index 3c06aea..d85e404 100644 --- a/rust/client/src/ui/theme/row.rs +++ b/rust/client/src/ui/theme/row.rs @@ -1,5 +1,4 @@ use iced::widget::Row; -use iced::Padding; use iced::Renderer; use crate::ui::theme::get_theme; diff --git a/rust/client/src/ui/widget/data.rs b/rust/client/src/ui/widget/data.rs index 1323581..2dd6d09 100644 --- a/rust/client/src/ui/widget/data.rs +++ b/rust/client/src/ui/widget/data.rs @@ -36,7 +36,6 @@ use crate::ui::AppMsg; pub struct ComponentWidgets<'b> { pub root_widget: &'b Option>, pub state: &'b HashMap, - pub plugin_id: PluginId, pub data: &'b HashMap>, } @@ -44,13 +43,11 @@ impl<'b> ComponentWidgets<'b> { pub fn new( root_widget: &'b Option>, state: &'b HashMap, - plugin_id: PluginId, data: &'b HashMap>, ) -> ComponentWidgets<'b> { Self { root_widget, state, - plugin_id, data, } } diff --git a/rust/client/src/ui/widget/data_mut.rs b/rust/client/src/ui/widget/data_mut.rs index 975db7e..16a5337 100644 --- a/rust/client/src/ui/widget/data_mut.rs +++ b/rust/client/src/ui/widget/data_mut.rs @@ -25,7 +25,6 @@ pub struct ComponentWidgetsMut<'b> { pub root_widget: &'b mut Option>, pub state: &'b mut HashMap, pub plugin_id: PluginId, - pub data: &'b HashMap>, } impl<'b> ComponentWidgetsMut<'b> { @@ -33,16 +32,15 @@ impl<'b> ComponentWidgetsMut<'b> { root_widget: &'b mut Option>, state: &'b mut HashMap, plugin_id: PluginId, - data: &'b HashMap>, ) -> ComponentWidgetsMut<'b> { Self { root_widget, state, plugin_id, - data, } } + #[allow(unused)] pub fn text_field_state_mut(&mut self, widget_id: UiWidgetId) -> &mut TextFieldState { Self::text_field_state_mut_on_state(&mut self.state, widget_id) } diff --git a/rust/client/src/ui/widget_container.rs b/rust/client/src/ui/widget_container.rs index 5f90572..ce65b26 100644 --- a/rust/client/src/ui/widget_container.rs +++ b/rust/client/src/ui/widget_container.rs @@ -1,9 +1,7 @@ use std::collections::hash_map::Entry; use std::collections::HashMap; use std::mem; -use std::ops::DerefMut; use std::sync::Arc; -use std::sync::Mutex; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalShortcut; @@ -96,7 +94,7 @@ impl PluginWidgetContainer { self.root_widget = Some(container); if first_open { - ComponentWidgets::new(&mut self.root_widget, &mut self.state, plugin_id.clone(), &self.data).first_open() + ComponentWidgets::new(&mut self.root_widget, &mut self.state, &self.data).first_open() } else { AppMsg::Noop } @@ -110,72 +108,69 @@ impl PluginWidgetContainer { pub fn render_root_widget<'a>( &self, - plugin_id: PluginId, plugin_view_state: &PluginViewState, action_shortcuts: &HashMap, ) -> Element<'a, ComponentWidgetEvent> { - ComponentWidgets::new(&self.root_widget, &self.state, plugin_id, &self.data).render_root_widget( + ComponentWidgets::new(&self.root_widget, &self.state, &self.data).render_root_widget( plugin_view_state, self.entrypoint_name.as_ref(), action_shortcuts, ) } - pub fn render_inline_root_widget<'a>(&self, plugin_id: PluginId) -> Element<'a, ComponentWidgetEvent> { - ComponentWidgets::new(&self.root_widget, &self.state, plugin_id, &self.data) + pub fn render_inline_root_widget<'a>(&self) -> Element<'a, ComponentWidgetEvent> { + ComponentWidgets::new(&self.root_widget, &self.state, &self.data) .render_root_inline_widget(self.plugin_name.as_ref(), self.entrypoint_name.as_ref()) } pub fn append_text(&mut self, text: &str) -> Task { let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id, &self.data).append_text(text) + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).append_text(text) } pub fn backspace_text(&mut self) -> Task { let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id, &self.data).backspace_text() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).backspace_text() } pub fn focus_search_bar(&self, widget_id: UiWidgetId) -> Task { - let plugin_id = self.get_plugin_id(); - ComponentWidgets::new(&self.root_widget, &self.state, plugin_id, &self.data).focus_search_bar(widget_id) + ComponentWidgets::new(&self.root_widget, &self.state, &self.data).focus_search_bar(widget_id) } pub fn toggle_action_panel(&mut self) { let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id, &self.data).toggle_action_panel() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).toggle_action_panel() } pub fn get_action_ids(&self) -> Vec { - ComponentWidgets::new(&self.root_widget, &self.state, self.get_plugin_id(), &self.data).get_action_ids() + ComponentWidgets::new(&self.root_widget, &self.state, &self.data).get_action_ids() } pub fn get_focused_item_id(&self) -> Option { - ComponentWidgets::new(&self.root_widget, &self.state, self.get_plugin_id(), &self.data).get_focused_item_id() + ComponentWidgets::new(&self.root_widget, &self.state, &self.data).get_focused_item_id() } pub fn get_action_panel(&self, action_shortcuts: &HashMap) -> Option { - ComponentWidgets::new(&self.root_widget, &self.state, self.get_plugin_id(), &self.data) - .get_action_panel(action_shortcuts) + ComponentWidgets::new(&self.root_widget, &self.state, &self.data).get_action_panel(action_shortcuts) } pub fn focus_up(&mut self) -> Task { let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id, &self.data).focus_up() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).focus_up() } pub fn focus_down(&mut self) -> Task { let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id, &self.data).focus_down() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).focus_down() } pub fn focus_left(&mut self) -> Task { let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id, &self.data).focus_left() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).focus_left() } pub fn focus_right(&mut self) -> Task { let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id, &self.data).focus_right() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).focus_right() } } diff --git a/rust/common/src/dirs.rs b/rust/common/src/dirs.rs index 07a64dd..51ef30d 100644 --- a/rust/common/src/dirs.rs +++ b/rust/common/src/dirs.rs @@ -1,7 +1,6 @@ use std::path::Path; use std::path::PathBuf; -use anyhow::Context; use directories::BaseDirs; use directories::ProjectDirs; diff --git a/rust/common/src/rpc/frontend_api.rs b/rust/common/src/rpc/frontend_api.rs index d2ab935..cd33e32 100644 --- a/rust/common/src/rpc/frontend_api.rs +++ b/rust/common/src/rpc/frontend_api.rs @@ -75,17 +75,9 @@ pub trait FrontendApi { async fn open_generated_plugin_view( &self, plugin_id: PluginId, - plugin_name: String, entrypoint_id: EntrypointId, - entrypoint_name: String, action_index: usize, ) -> RequestResult<()>; - async fn open_plugin_view( - &self, - plugin_id: PluginId, - plugin_name: String, - entrypoint_id: EntrypointId, - entrypoint_name: String, - ) -> RequestResult<()>; + async fn open_plugin_view(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> RequestResult<()>; } diff --git a/rust/common_plugin_runtime/src/lib.rs b/rust/common_plugin_runtime/src/lib.rs index ce7771f..945e469 100644 --- a/rust/common_plugin_runtime/src/lib.rs +++ b/rust/common_plugin_runtime/src/lib.rs @@ -5,30 +5,12 @@ use std::sync::atomic::Ordering; use anyhow::Context; use bincode::Decode; use bincode::Encode; -use gauntlet_utils::channel::Payload; -use gauntlet_utils::channel::RequestReceiver; -use interprocess::local_socket::tokio::prelude::*; use interprocess::local_socket::tokio::RecvHalf; use interprocess::local_socket::tokio::SendHalf; -use interprocess::local_socket::tokio::Stream; -use interprocess::local_socket::GenericFilePath; -use interprocess::local_socket::NameType; -use interprocess::local_socket::ToNsName; use once_cell::sync::Lazy; use regex::Regex; -use serde::de::DeserializeOwned; -use serde::Deserialize; -use serde::Serialize; -use tokio::io::AsyncBufReadExt; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; -use tokio::runtime::Handle; -use tokio::sync::mpsc::channel; -use tokio::sync::mpsc::Receiver; -use tokio::sync::mpsc::Sender; -use tokio::sync::oneshot; -use tokio::sync::Mutex; -use tokio::sync::MutexGuard; pub mod api; pub mod model; diff --git a/rust/common_ui/src/lib.rs b/rust/common_ui/src/lib.rs index de24bfc..1cd24ca 100644 --- a/rust/common_ui/src/lib.rs +++ b/rust/common_ui/src/lib.rs @@ -7,7 +7,6 @@ use iced::widget::value; use iced::Element; use iced::Padding; use iced::Pixels; -use iced_aw::iced_fonts::bootstrap; use iced_aw::iced_fonts::Bootstrap; use iced_aw::iced_fonts::BOOTSTRAP_FONT; diff --git a/rust/component_model/src/lib.rs b/rust/component_model/src/lib.rs index b52233a..9df30ab 100644 --- a/rust/component_model/src/lib.rs +++ b/rust/component_model/src/lib.rs @@ -178,6 +178,7 @@ pub struct ComponentRef { pub arity: Arity, } +#[allow(unused)] fn children_string_or_members(ordered_members: I1, per_type_members: I2) -> Children where I1: IntoIterator, diff --git a/rust/management_client/src/components/shortcut_selector.rs b/rust/management_client/src/components/shortcut_selector.rs index 5c2453f..28c2658 100644 --- a/rust/management_client/src/components/shortcut_selector.rs +++ b/rust/management_client/src/components/shortcut_selector.rs @@ -63,18 +63,15 @@ where )) } -pub struct ShortcutSelector<'a, 'b, Message> { +pub struct ShortcutSelector<'a, Message> { on_shortcut_captured: Box) -> Message + 'a>, - - current_shortcut: &'b ShortcutData, - content: Element<'a, Message>, popup: Element<'a, Message>, overlay_class: ::Class<'a>, in_table: bool, } -impl<'a, 'b, 'c, Message: 'a> ShortcutSelector<'a, 'b, Message> { +impl<'a, 'b, 'c, Message: 'a> ShortcutSelector<'a, Message> { pub fn new( current_shortcut: &'b ShortcutData, on_shortcut_captured: F, @@ -119,7 +116,6 @@ impl<'a, 'b, 'c, Message: 'a> ShortcutSelector<'a, 'b, Message> { Self { on_shortcut_captured: Box::new(on_shortcut_captured), - current_shortcut, content, popup, @@ -135,7 +131,7 @@ struct State { is_hovering: bool, } -impl<'a, 'b, Message: 'a> Widget for ShortcutSelector<'a, 'b, Message> { +impl<'a, 'b, Message: 'a> Widget for ShortcutSelector<'a, Message> { fn size(&self) -> Size { Size { width: Length::Fill, diff --git a/rust/management_client/src/theme.rs b/rust/management_client/src/theme.rs index bcd118d..d4d5d74 100644 --- a/rust/management_client/src/theme.rs +++ b/rust/management_client/src/theme.rs @@ -28,14 +28,17 @@ impl DefaultStyle for GauntletSettingsTheme { } // keep colors more or less in sync with main ui +#[allow(unused)] pub const NOT_INTENDED_TO_BE_USED: ThemeColor = ThemeColor::new(0xAF5BFF, 1.0); pub const TRANSPARENT: ThemeColor = ThemeColor::new(0x000000, 0.0); +#[allow(unused)] pub const BACKGROUND_LIGHTEST: ThemeColor = ThemeColor::new(0x626974, 0.3); pub const BACKGROUND_LIGHTER: ThemeColor = ThemeColor::new(0x48505B, 0.5); pub const BACKGROUND_DARKER: ThemeColor = ThemeColor::new(0x333a42, 1.0); pub const BACKGROUND_DARKEST: ThemeColor = ThemeColor::new(0x2C323A, 1.0); pub const TEXT_LIGHTEST: ThemeColor = ThemeColor::new(0xDDDFE1, 1.0); +#[allow(unused)] pub const TEXT_LIGHTER: ThemeColor = ThemeColor::new(0x9AA0A6, 1.0); pub const TEXT_DARKER: ThemeColor = ThemeColor::new(0x6B7785, 1.0); pub const TEXT_DARKEST: ThemeColor = ThemeColor::new(0x1D242C, 1.0); diff --git a/rust/management_client/src/theme/button.rs b/rust/management_client/src/theme/button.rs index 91f1ee9..310b2e7 100644 --- a/rust/management_client/src/theme/button.rs +++ b/rust/management_client/src/theme/button.rs @@ -16,6 +16,7 @@ use crate::theme::TEXT_LIGHTEST; pub enum ButtonStyle { Primary, + #[allow(unused)] Positive, Destructive, TableRow, diff --git a/rust/management_client/src/theme/container.rs b/rust/management_client/src/theme/container.rs index 9b8ac84..dc562a2 100644 --- a/rust/management_client/src/theme/container.rs +++ b/rust/management_client/src/theme/container.rs @@ -6,14 +6,12 @@ use iced::Color; use crate::theme::GauntletSettingsTheme; use crate::theme::BACKGROUND_DARKER; use crate::theme::BACKGROUND_LIGHTER; -use crate::theme::BACKGROUND_LIGHTEST; use crate::theme::DANGER; use crate::theme::TRANSPARENT; pub enum ContainerStyle { Transparent, Box, - TextInputLike, TextInputMissingValue, } @@ -38,17 +36,6 @@ impl container::Catalog for GauntletSettingsTheme { ..Default::default() } } - ContainerStyle::TextInputLike => { - Style { - background: Some(BACKGROUND_LIGHTEST.to_iced().into()), - border: Border { - radius: 4.0.into(), - width: 1.0, - color: BACKGROUND_LIGHTEST.to_iced().into(), - }, - ..Default::default() - } - } ContainerStyle::TextInputMissingValue => { let color = DANGER.to_iced(); diff --git a/rust/management_client/src/ui.rs b/rust/management_client/src/ui.rs index 37d72f4..ce027e7 100644 --- a/rust/management_client/src/ui.rs +++ b/rust/management_client/src/ui.rs @@ -97,7 +97,7 @@ pub enum ManagementAppMsg { } #[derive(Debug, Clone, PartialEq, Eq)] -enum SettingsView { +pub enum SettingsView { General, Plugins, } @@ -255,7 +255,7 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task Task Task { - let mut backend_client = backend_api.clone(); + let backend_client = backend_api.clone(); let already_downloading = state .downloads_info @@ -618,10 +618,10 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { let content: Element<_> = column(vec![top_bar, separator, content]).into(); let download_info_panel: Element<_> = { - let downloads: Vec> = state + let downloads = state .downloads_info .iter() - .sorted_by_key(|(_, info)| info.clone()) + .sorted_by_key(|&(_, &ref info)| info) .map(|(plugin_id, info)| { match info { DownloadInfo::InProgress => { @@ -706,9 +706,11 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { container(content).width(Length::Fill).into() } } - }) - .intersperse_with(|| horizontal_rule(1).into()) - .collect(); + }); + + let downloads = Itertools::intersperse_with(downloads, || horizontal_rule(1).into()); + + let downloads: Vec> = downloads.collect(); let downloads: Element<_> = column(downloads).into(); diff --git a/rust/management_client/src/views/general.rs b/rust/management_client/src/views/general.rs index 8a4cead..9a0251a 100644 --- a/rust/management_client/src/views/general.rs +++ b/rust/management_client/src/views/general.rs @@ -77,7 +77,7 @@ impl ManagementAppGeneralState { match message { ManagementAppGeneralMsgIn::ShortcutCaptured(shortcut) => { - let mut backend_api = backend_api.clone(); + let backend_api = backend_api.clone(); Task::perform( { @@ -120,7 +120,7 @@ impl ManagementAppGeneralState { ManagementAppGeneralMsgIn::ThemeChanged(theme) => { self.theme = theme.clone(); - let mut backend_api = backend_api.clone(); + let backend_api = backend_api.clone(); Task::perform( async move { @@ -136,7 +136,7 @@ impl ManagementAppGeneralState { ManagementAppGeneralMsgIn::WindowPositionModeChanged(mode) => { self.window_position_mode = mode.clone(); - let mut backend_api = backend_api.clone(); + let backend_api = backend_api.clone(); Task::perform( async move { @@ -184,7 +184,7 @@ impl ManagementAppGeneralState { let theme_field = self.theme_field(); - let mut content = vec![global_shortcut_field, theme_field]; + let content = vec![global_shortcut_field, theme_field]; #[cfg(target_os = "macos")] { @@ -242,6 +242,7 @@ impl ManagementAppGeneralState { theme_field } + #[allow(unused)] fn window_position_mode_field(&self) -> Element { let items = [WindowPositionMode::Static, WindowPositionMode::ActiveMonitor]; diff --git a/rust/management_client/src/views/plugins.rs b/rust/management_client/src/views/plugins.rs index ce5c103..231d3fb 100644 --- a/rust/management_client/src/views/plugins.rs +++ b/rust/management_client/src/views/plugins.rs @@ -10,18 +10,14 @@ use gauntlet_common::model::SettingsEntrypointType; use gauntlet_common::model::SettingsPlugin; use gauntlet_common::rpc::backend_api::BackendForSettingsApi; use gauntlet_common::rpc::backend_api::BackendForSettingsApiProxy; -use gauntlet_common::rpc::backend_api::GrpcBackendApi; use gauntlet_common::settings_env_data_from_string; use gauntlet_common::SettingsEnvData; use gauntlet_common::SETTINGS_ENV; -use gauntlet_utils::channel::RequestError; use gauntlet_utils::channel::RequestResult; -use iced::alignment; use iced::padding; use iced::widget::button; use iced::widget::column; use iced::widget::container; -use iced::widget::pane_grid::Edge; use iced::widget::row; use iced::widget::scrollable; use iced::widget::text; @@ -77,7 +73,6 @@ pub enum ManagementAppPluginMsgIn { plugin_id: PluginId, }, SelectItem(SelectedItem), - Noop, } pub enum ManagementAppPluginMsgOut { @@ -151,7 +146,7 @@ impl ManagementAppPluginsState { self.table_state.update(message).then(move |msg| { match msg { PluginTableMsgOut::SetPluginState { enabled, plugin_id } => { - let mut backend_client = backend_api.clone(); + let backend_client = backend_api.clone(); Task::perform( async move { @@ -234,7 +229,7 @@ impl ManagementAppPluginsState { )) } PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) => { - let mut backend_client = backend_api.clone(); + let backend_client = backend_api.clone(); Task::perform( async move { @@ -264,7 +259,7 @@ impl ManagementAppPluginsState { ) } PluginTableMsgOut::AliasChanged(plugin_id, entrypoint_id, shortcut) => { - let mut backend_client = backend_api.clone(); + let backend_client = backend_api.clone(); Task::perform( async move { @@ -351,7 +346,7 @@ impl ManagementAppPluginsState { user_data.clone(), ); - let mut backend_api = backend_api.clone(); + let backend_api = backend_api.clone(); Task::perform( async move { @@ -371,7 +366,7 @@ impl ManagementAppPluginsState { } } ManagementAppPluginMsgIn::FetchPlugins => { - let mut backend_api = backend_api.clone(); + let backend_api = backend_api.clone(); Task::perform( async move { @@ -400,7 +395,7 @@ impl ManagementAppPluginsState { ManagementAppPluginMsgIn::RemovePlugin { plugin_id } => { self.selected_item = SelectedItem::None; - let mut backend_client = backend_api.clone(); + let backend_client = backend_api.clone(); Task::perform( async move { @@ -433,7 +428,6 @@ impl ManagementAppPluginsState { Task::none() } - ManagementAppPluginMsgIn::Noop => Task::none(), } } diff --git a/rust/management_client/src/views/plugins/preferences.rs b/rust/management_client/src/views/plugins/preferences.rs index de7a450..3e8e1b9 100644 --- a/rust/management_client/src/views/plugins/preferences.rs +++ b/rust/management_client/src/views/plugins/preferences.rs @@ -60,7 +60,7 @@ pub fn preferences_ui<'a>( let mut preferences: Vec<_> = preferences.iter().map(|entry| entry).collect(); - preferences.sort_by_key(|(&ref key, _)| key); + preferences.sort_by_key(|&(&ref key, _)| key); for (preference_id, preference) in preferences { let plugin_id = plugin_id.clone(); diff --git a/rust/management_client/src/views/plugins/table.rs b/rust/management_client/src/views/plugins/table.rs index d6562ab..ef0da2e 100644 --- a/rust/management_client/src/views/plugins/table.rs +++ b/rust/management_client/src/views/plugins/table.rs @@ -32,7 +32,6 @@ use crate::components::shortcut_selector::shortcut_selector; use crate::components::shortcut_selector::ShortcutData; use crate::theme::button::ButtonStyle; use crate::theme::container::ContainerStyle; -use crate::theme::text::TextStyle; use crate::theme::text_input::TextInputStyle; use crate::theme::Element; use crate::theme::GauntletSettingsTheme; @@ -248,7 +247,7 @@ impl PluginTableState { } #[derive(Debug, Clone)] -enum EnabledItem { +pub enum EnabledItem { Plugin { enabled: bool, plugin_id: PluginId, diff --git a/rust/plugin_runtime/Cargo.toml b/rust/plugin_runtime/Cargo.toml index 92c608b..8d5c307 100644 --- a/rust/plugin_runtime/Cargo.toml +++ b/rust/plugin_runtime/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition.workspace = true [workspace.package] -edition = "2021" +edition = "2024" [lib] crate-type = ["rlib"] diff --git a/rust/plugin_runtime/src/deno.rs b/rust/plugin_runtime/src/deno.rs index 337ba72..d1bf833 100644 --- a/rust/plugin_runtime/src/deno.rs +++ b/rust/plugin_runtime/src/deno.rs @@ -2,14 +2,11 @@ use std::collections::HashMap; use std::fs::File; use std::path::Path; use std::path::PathBuf; -use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; -use anyhow::anyhow; use anyhow::Context; -use deno_core::futures::Stream; -use deno_core::url::Url; +use anyhow::anyhow; use deno_core::FastString; use deno_core::ModuleLoadResponse; use deno_core::ModuleLoader; @@ -20,6 +17,8 @@ use deno_core::ModuleType; use deno_core::RequestedModuleType; use deno_core::ResolutionKind; use deno_core::StaticModuleLoader; +use deno_core::url::Url; +use deno_runtime::BootstrapOptions; use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_io::Stdio; @@ -27,8 +26,6 @@ use deno_runtime::deno_io::StdioPipe; use deno_runtime::worker::MainWorker; use deno_runtime::worker::WorkerOptions; use deno_runtime::worker::WorkerServiceOptions; -use deno_runtime::BootstrapOptions; -use gauntlet_common::model::PluginId; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use gauntlet_common_plugin_runtime::model::JsEvent; use gauntlet_common_plugin_runtime::model::JsInit; @@ -51,8 +48,8 @@ use crate::environment::environment_gauntlet_version; use crate::environment::environment_is_development; use crate::environment::environment_plugin_cache_dir; use crate::environment::environment_plugin_data_dir; -use crate::events::op_plugin_get_pending_event; use crate::events::EventReceiver; +use crate::events::op_plugin_get_pending_event; use crate::logs::op_log_debug; use crate::logs::op_log_error; use crate::logs::op_log_info; @@ -60,11 +57,11 @@ use crate::logs::op_log_trace; use crate::logs::op_log_warn; use crate::permissions::permissions_to_deno; use crate::plugin_data::PluginData; +use crate::plugins::applications::ApplicationContext; use crate::plugins::applications::current_os; use crate::plugins::applications::wayland; -use crate::plugins::applications::ApplicationContext; -use crate::plugins::numbat::run_numbat; use crate::plugins::numbat::NumbatContext; +use crate::plugins::numbat::run_numbat; use crate::plugins::settings::open_settings; use crate::preferences::entrypoint_preferences_required; use crate::preferences::get_entrypoint_preferences; @@ -86,6 +83,7 @@ use crate::ui::update_loading_bar; pub struct CustomModuleLoader { code: JsPluginCode, static_loader: StaticModuleLoader, + #[allow(unused)] dev_plugin: bool, } @@ -227,7 +225,7 @@ impl ModuleLoader for CustomModuleLoader { "Illegal import with specifier '{}' and referrer '{}'", specifier, referrer - )) + )); } }; diff --git a/rust/plugin_runtime/src/events.rs b/rust/plugin_runtime/src/events.rs index 75b12d5..d007385 100644 --- a/rust/plugin_runtime/src/events.rs +++ b/rust/plugin_runtime/src/events.rs @@ -1,18 +1,10 @@ use std::cell::RefCell; -use std::pin::Pin; use std::rc::Rc; use anyhow::anyhow; -use bincode::Decode; -use bincode::Encode; -use deno_core::futures::Stream; -use deno_core::futures::StreamExt; -use deno_core::op2; use deno_core::OpState; -use gauntlet_common::model::UiWidgetId; +use deno_core::op2; use gauntlet_common_plugin_runtime::model::JsEvent; -use serde::Deserialize; -use serde::Serialize; use tokio::sync::mpsc::Receiver; pub struct EventReceiver { diff --git a/rust/plugin_runtime/src/lib.rs b/rust/plugin_runtime/src/lib.rs index 028409a..224b8cd 100644 --- a/rust/plugin_runtime/src/lib.rs +++ b/rust/plugin_runtime/src/lib.rs @@ -14,22 +14,10 @@ mod preferences; mod search; mod ui; -use std::cell::RefCell; -use std::cell::RefMut; -use std::convert; -use std::fmt::Debug; use std::ops::Deref; -use std::ops::DerefMut; -use std::sync::atomic::AtomicU32; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; use anyhow::anyhow; -use anyhow::Context; -use bincode::Decode; -use bincode::Encode; -use deno_core::futures::SinkExt; +use gauntlet_common_plugin_runtime::JsMessageSide; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiRequestData; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiResponseData; @@ -39,31 +27,17 @@ use gauntlet_common_plugin_runtime::model::JsMessage; use gauntlet_common_plugin_runtime::model::JsPluginRuntimeMessage; use gauntlet_common_plugin_runtime::recv_message; use gauntlet_common_plugin_runtime::send_message; -use gauntlet_common_plugin_runtime::JsMessageSide; -use gauntlet_utils::channel::Payload; use gauntlet_utils::channel::RequestReceiver; -use interprocess::local_socket::tokio::prelude::*; use interprocess::local_socket::tokio::RecvHalf; use interprocess::local_socket::tokio::SendHalf; use interprocess::local_socket::tokio::Stream; -use interprocess::local_socket::GenericFilePath; -use interprocess::local_socket::NameType; -use interprocess::local_socket::ToNsName; -use once_cell::sync::Lazy; -use regex::Regex; -use serde::de::DeserializeOwned; -use serde::Deserialize; -use serde::Serialize; -use tokio::io::AsyncBufReadExt; -use tokio::io::AsyncReadExt; -use tokio::io::AsyncWriteExt; +use interprocess::local_socket::tokio::prelude::*; use tokio::runtime::Handle; -use tokio::sync::mpsc::channel; +use tokio::sync::Mutex; use tokio::sync::mpsc::Receiver; use tokio::sync::mpsc::Sender; +use tokio::sync::mpsc::channel; use tokio::sync::oneshot; -use tokio::sync::Mutex; -use tokio::sync::MutexGuard; use tokio_util::sync::CancellationToken; use crate::deno::start_js_runtime; @@ -116,7 +90,7 @@ async fn run_outer(socket_name: String) -> anyhow::Result<()> { _ = stop_token.cancelled() => { tracing::debug!("Plugin runtime outer loop will be stopped {:?}", plugin_id) } - result @ _ = { + _ = { tokio::task::unconstrained(async { loop { if let Err(err) = message_loop(&mut recver, &event_sender, &response_oneshot, stop_token.clone()).await { @@ -128,7 +102,7 @@ async fn run_outer(socket_name: String) -> anyhow::Result<()> { } => { tracing::error!("Message loop has unexpectedly stopped {:?}", plugin_id) } - result @ _ = { + _ = { tokio::task::unconstrained(async { loop { if let Err(err) = request_loop(&mut sender, &mut request_receiver, &response_oneshot).await { @@ -140,7 +114,7 @@ async fn run_outer(socket_name: String) -> anyhow::Result<()> { } => { tracing::error!("Request loop has unexpectedly stopped {:?}", plugin_id) } - result @ _ = { + _ = { run_new_tokio(handle, stop_token.clone(), init, event_receiver, api) } => { tracing::error!("Request loop has unexpectedly stopped {:?}", plugin_id) @@ -283,7 +257,7 @@ async fn message_loop( let mut response_oneshot = response_oneshot.lock().await; match response_oneshot.take() { - Some(mut oneshot) => { + Some(oneshot) => { match oneshot.send(response) { Err(_) => { tracing::error!("Dropped oneshot receiving side"); diff --git a/rust/plugin_runtime/src/permissions.rs b/rust/plugin_runtime/src/permissions.rs index fe66476..27fd52e 100644 --- a/rust/plugin_runtime/src/permissions.rs +++ b/rust/plugin_runtime/src/permissions.rs @@ -2,12 +2,10 @@ use std::collections::HashSet; use std::hash::Hash; use std::path::Path; use std::path::PathBuf; -use std::str::FromStr; use std::sync::Arc; use anyhow::anyhow; use deno_runtime::deno_fs::FileSystemRc; -use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_permissions::AllowRunDescriptor; use deno_runtime::deno_permissions::EnvDescriptor; use deno_runtime::deno_permissions::EnvQueryDescriptor; @@ -18,16 +16,12 @@ use deno_runtime::deno_permissions::QueryDescriptor; use deno_runtime::deno_permissions::ReadDescriptor; use deno_runtime::deno_permissions::RunQueryDescriptor; use deno_runtime::deno_permissions::SysDescriptor; -use deno_runtime::deno_permissions::SysDescriptorParseError; use deno_runtime::deno_permissions::UnaryPermission; use deno_runtime::deno_permissions::WriteDescriptor; use deno_runtime::permissions::RuntimePermissionDescriptorParser; -use gauntlet_common::dirs::Dirs; +use gauntlet_common_plugin_runtime::PERMISSIONS_VARIABLE_PATTERN; use gauntlet_common_plugin_runtime::model::JsPluginPermissions; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsExec; -use gauntlet_common_plugin_runtime::PERMISSIONS_VARIABLE_PATTERN; -use once_cell::sync::Lazy; -use regex::Regex; use typed_path::Utf8TypedPath; pub fn permissions_to_deno( diff --git a/rust/plugin_runtime/src/plugin_data.rs b/rust/plugin_runtime/src/plugin_data.rs index 58c0a2b..ee31c50 100644 --- a/rust/plugin_runtime/src/plugin_data.rs +++ b/rust/plugin_runtime/src/plugin_data.rs @@ -44,6 +44,7 @@ impl PluginData { self.plugin_id.clone() } + #[allow(unused)] pub fn plugin_uuid(&self) -> &str { &self.plugin_uuid } diff --git a/rust/plugin_runtime/src/plugins/applications.rs b/rust/plugin_runtime/src/plugins/applications.rs index 0d7dd23..1fdd56b 100644 --- a/rust/plugin_runtime/src/plugins/applications.rs +++ b/rust/plugin_runtime/src/plugins/applications.rs @@ -1,22 +1,12 @@ use std::cell::RefCell; -use std::path::PathBuf; use std::rc::Rc; -use anyhow::anyhow; -use deno_core::op2; use deno_core::OpState; use deno_core::ToJsBuffer; -use gauntlet_common::detached_process::CommandExt; -use image::imageops::FilterType; +use deno_core::op2; use image::ImageFormat; -use serde::Deserialize; +use image::imageops::FilterType; use serde::Serialize; -use sys_locale::get_locale; -use tokio::runtime::Handle; -use tokio::sync::mpsc::Receiver; -use tokio::task::spawn_blocking; - -use crate::plugin_data::PluginData; #[cfg(target_os = "linux")] mod linux; @@ -99,6 +89,7 @@ pub fn wayland(state: Rc>) -> bool { pub enum DesktopEnvironment { #[cfg(target_os = "linux")] Linux(linux::LinuxDesktopEnvironment), + #[allow(unused)] None, } @@ -226,7 +217,7 @@ pub fn macos_open_setting_pre_13(#[string] setting_path: String) -> anyhow::Resu #[op2] #[string] pub fn macos_get_localized_language() -> Option { - get_locale()? + sys_locale::get_locale()? .split("-") .collect::>() .get(0) diff --git a/rust/plugin_runtime/src/plugins/applications/linux/mod.rs b/rust/plugin_runtime/src/plugins/applications/linux/mod.rs index 7b64a1b..236cd38 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/mod.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/mod.rs @@ -1,28 +1,19 @@ use std::cell::RefCell; -use std::collections::HashMap; -use std::collections::HashSet; -use std::fs::Metadata; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; -use deno_core::op2; use deno_core::OpState; -use deno_core::ToJsBuffer; +use deno_core::op2; use freedesktop_entry_parser::parse_entry; use freedesktop_icons::lookup; use gauntlet_common::detached_process::CommandExt; -use image::imageops::FilterType; -use image::ImageFormat; -use tokio::sync::mpsc::Sender; use tokio::task::spawn_blocking; -use walkdir::WalkDir; use crate::plugin_data::PluginData; -use crate::plugins::applications::linux; -use crate::plugins::applications::resize_icon; use crate::plugins::applications::DesktopApplication; use crate::plugins::applications::DesktopPathAction; +use crate::plugins::applications::resize_icon; mod wayland; mod x11; diff --git a/rust/plugin_runtime/src/plugins/applications/linux/wayland/cosmic.rs b/rust/plugin_runtime/src/plugins/applications/linux/wayland/cosmic.rs index a9ac496..0a55c60 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/wayland/cosmic.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/wayland/cosmic.rs @@ -1,29 +1,22 @@ -use std::cell::RefCell; use std::collections::HashMap; -use std::rc::Rc; -use std::sync::Arc; -use std::sync::Mutex; use anyhow::anyhow; use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1; use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_info_v1; use cosmic_protocols::toplevel_management::v1::client::zcosmic_toplevel_manager_v1; -use smithay_client_toolkit::reexports::calloop::channel::Sender; use smithay_client_toolkit::seat::SeatState; -use tokio::runtime::Handle; -use wayland_client::backend::ObjectId; -use wayland_client::event_created_child; -use wayland_client::globals::GlobalList; -use wayland_client::protocol::wl_seat::WlSeat; use wayland_client::Connection; use wayland_client::Dispatch; use wayland_client::Proxy; use wayland_client::QueueHandle; +use wayland_client::backend::ObjectId; +use wayland_client::event_created_child; +use wayland_client::globals::GlobalList; -use crate::plugins::applications::linux::wayland::send_event; use crate::plugins::applications::linux::wayland::JsWaylandApplicationEvent; use crate::plugins::applications::linux::wayland::WaylandState; use crate::plugins::applications::linux::wayland::WaylandStateInner; +use crate::plugins::applications::linux::wayland::send_event; pub struct CosmicWaylandState { uuid_to_obj_id: HashMap, diff --git a/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs b/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs index 5a582ca..ec830f0 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs @@ -3,16 +3,14 @@ use std::rc::Rc; use std::thread; use anyhow::anyhow; -use deno_core::op2; use deno_core::OpState; +use deno_core::op2; use serde::Deserialize; use serde::Serialize; use smithay_client_toolkit::reexports::calloop; +use smithay_client_toolkit::reexports::calloop::EventLoop; use smithay_client_toolkit::reexports::calloop::channel::Channel; use smithay_client_toolkit::reexports::calloop::channel::Event; -use smithay_client_toolkit::reexports::calloop::EventLoop; -use smithay_client_toolkit::reexports::calloop::InsertError; -use smithay_client_toolkit::reexports::calloop::RegistrationToken; use smithay_client_toolkit::reexports::calloop_wayland_source::WaylandSource; use smithay_client_toolkit::seat::Capability; use smithay_client_toolkit::seat::SeatHandler; @@ -20,18 +18,18 @@ use smithay_client_toolkit::seat::SeatState; use tokio::runtime::Handle; use tokio::sync::mpsc::Receiver; use tokio::sync::mpsc::Sender; -use wayland_client::globals::registry_queue_init; -use wayland_client::globals::GlobalList; -use wayland_client::globals::GlobalListContents; -use wayland_client::protocol::wl_registry; -use wayland_client::protocol::wl_seat::WlSeat; use wayland_client::Connection; use wayland_client::Dispatch; use wayland_client::QueueHandle; +use wayland_client::globals::GlobalList; +use wayland_client::globals::GlobalListContents; +use wayland_client::globals::registry_queue_init; +use wayland_client::protocol::wl_registry; +use wayland_client::protocol::wl_seat::WlSeat; -use crate::plugins::applications::linux; use crate::plugins::applications::ApplicationContext; use crate::plugins::applications::DesktopEnvironment; +use crate::plugins::applications::linux; mod cosmic; mod wlr; @@ -98,7 +96,7 @@ impl WaylandState { ) -> anyhow::Result { let inner = wlr::WlrWaylandState::new(globals, queue_handle) .map(|state| WaylandStateInner::Wlr(state)) - .or_else(|test| { + .or_else(|_| { cosmic::CosmicWaylandState::new(globals, queue_handle).map(|state| WaylandStateInner::Cosmic(state)) }) .unwrap_or(WaylandStateInner::None); diff --git a/rust/plugin_runtime/src/plugins/applications/linux/wayland/wlr.rs b/rust/plugin_runtime/src/plugins/applications/linux/wayland/wlr.rs index 2b66ce9..4adfc74 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/wayland/wlr.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/wayland/wlr.rs @@ -1,28 +1,21 @@ -use std::cell::RefCell; use std::collections::HashMap; -use std::rc::Rc; -use std::sync::Arc; -use std::sync::Mutex; use anyhow::anyhow; -use smithay_client_toolkit::reexports::calloop::channel::Sender; use smithay_client_toolkit::seat::SeatState; -use tokio::runtime::Handle; -use wayland_client::backend::ObjectId; -use wayland_client::event_created_child; -use wayland_client::globals::GlobalList; -use wayland_client::protocol::wl_seat::WlSeat; use wayland_client::Connection; use wayland_client::Dispatch; use wayland_client::Proxy; use wayland_client::QueueHandle; +use wayland_client::backend::ObjectId; +use wayland_client::event_created_child; +use wayland_client::globals::GlobalList; use wayland_protocols_wlr::foreign_toplevel::v1::client::zwlr_foreign_toplevel_handle_v1; use wayland_protocols_wlr::foreign_toplevel::v1::client::zwlr_foreign_toplevel_manager_v1; -use crate::plugins::applications::linux::wayland::send_event; use crate::plugins::applications::linux::wayland::JsWaylandApplicationEvent; use crate::plugins::applications::linux::wayland::WaylandState; use crate::plugins::applications::linux::wayland::WaylandStateInner; +use crate::plugins::applications::linux::wayland::send_event; pub struct WlrWaylandState { uuid_to_obj_id: HashMap, diff --git a/rust/plugin_runtime/src/plugins/applications/linux/x11.rs b/rust/plugin_runtime/src/plugins/applications/linux/x11.rs index 341eae7..589b5c1 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/x11.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/x11.rs @@ -1,13 +1,12 @@ use std::cell::RefCell; -use std::collections::HashMap; use std::convert::Infallible; use std::rc::Rc; use std::str::FromStr; use std::thread; use anyhow::anyhow; -use deno_core::op2; use deno_core::OpState; +use deno_core::op2; use encoding::DecoderTrap; use encoding::Encoding; use serde::Deserialize; @@ -16,27 +15,22 @@ use tokio::runtime::Handle; use tokio::sync::mpsc::Receiver; use tokio::sync::mpsc::Sender; use x11rb::connection::Connection; -use x11rb::errors::ConnectionError; use x11rb::properties::WmClass; use x11rb::properties::WmHints; +use x11rb::protocol::Event; use x11rb::protocol::xproto::Atom; use x11rb::protocol::xproto::AtomEnum; use x11rb::protocol::xproto::ChangeWindowAttributesAux; use x11rb::protocol::xproto::ClientMessageEvent; -use x11rb::protocol::xproto::ConfigureWindowAux; use x11rb::protocol::xproto::ConnectionExt; use x11rb::protocol::xproto::EventMask; -use x11rb::protocol::xproto::InputFocus; use x11rb::protocol::xproto::MapState; -use x11rb::protocol::xproto::StackMode; use x11rb::protocol::xproto::Window; -use x11rb::protocol::Event; use x11rb::rust_connection::RustConnection; -use crate::plugins::applications::linux; -use crate::plugins::applications::linux::x11; use crate::plugins::applications::ApplicationContext; use crate::plugins::applications::DesktopEnvironment; +use crate::plugins::applications::linux; pub struct X11DesktopEnvironment { receiver: Rc>>, @@ -51,9 +45,9 @@ impl X11DesktopEnvironment { thread::Builder::new() .name("gauntlet-x11-events".to_string()) .spawn(move || { - if let Err(e) = listen_on_x11_events(handle, sender.clone()) { - tracing::error!("Error while listening on x11 events: {}", e); - } + let Err(e) = listen_on_x11_events(handle, sender.clone()); + + tracing::error!("Error while listening on x11 events: {}", e); }) .expect("failed to spawn thread"); @@ -65,7 +59,7 @@ impl X11DesktopEnvironment { #[derive(Debug, Deserialize, Serialize)] #[serde(tag = "type")] -enum JsX11ApplicationEvent { +pub enum JsX11ApplicationEvent { Init { id: String, parent_id: String, @@ -121,13 +115,13 @@ enum JsX11ApplicationEvent { } #[derive(Debug, Deserialize, Serialize)] -enum JSX11WindowProtocol { +pub enum JSX11WindowProtocol { TakeFocus, DeleteWindow, } #[derive(Debug, Deserialize, Serialize)] -enum JSX11WindowType { +pub enum JSX11WindowType { DropdownMenu, Dialog, Menu, diff --git a/rust/plugin_runtime/src/ui.rs b/rust/plugin_runtime/src/ui.rs index 3c1d7c3..787b5f1 100644 --- a/rust/plugin_runtime/src/ui.rs +++ b/rust/plugin_runtime/src/ui.rs @@ -1,97 +1,19 @@ use std::cell::RefCell; use std::collections::HashMap; -use std::io::Read; use std::rc::Rc; -use anyhow::anyhow; -use anyhow::Context; +use deno_core::OpState; use deno_core::op2; use deno_core::serde_v8; use deno_core::v8; -use deno_core::OpState; use futures::executor::block_on; -use gauntlet_common::model::ActionPanelSectionWidget; -use gauntlet_common::model::ActionPanelSectionWidgetOrderedMembers; -use gauntlet_common::model::ActionPanelWidget; -use gauntlet_common::model::ActionPanelWidgetOrderedMembers; -use gauntlet_common::model::ActionWidget; -use gauntlet_common::model::CheckboxWidget; -use gauntlet_common::model::CodeBlockWidget; -use gauntlet_common::model::ContentWidget; -use gauntlet_common::model::ContentWidgetOrderedMembers; -use gauntlet_common::model::DataSource; -use gauntlet_common::model::DataSourceAsset; -use gauntlet_common::model::DataSourceUrl; -use gauntlet_common::model::DatePickerWidget; -use gauntlet_common::model::DetailWidget; -use gauntlet_common::model::EmptyViewWidget; use gauntlet_common::model::EntrypointId; -use gauntlet_common::model::FormWidget; -use gauntlet_common::model::FormWidgetOrderedMembers; -use gauntlet_common::model::GridItemWidget; -use gauntlet_common::model::GridSectionWidget; -use gauntlet_common::model::GridSectionWidgetOrderedMembers; -use gauntlet_common::model::GridWidget; -use gauntlet_common::model::GridWidgetOrderedMembers; -use gauntlet_common::model::H1Widget; -use gauntlet_common::model::H2Widget; -use gauntlet_common::model::H3Widget; -use gauntlet_common::model::H4Widget; -use gauntlet_common::model::H5Widget; -use gauntlet_common::model::H6Widget; -use gauntlet_common::model::HorizontalBreakWidget; -use gauntlet_common::model::IconAccessoryWidget; -use gauntlet_common::model::ImageLike; -use gauntlet_common::model::ImageWidget; -use gauntlet_common::model::InlineSeparatorWidget; -use gauntlet_common::model::InlineWidget; -use gauntlet_common::model::InlineWidgetOrderedMembers; -use gauntlet_common::model::ListItemAccessories; -use gauntlet_common::model::ListItemWidget; -use gauntlet_common::model::ListSectionWidget; -use gauntlet_common::model::ListSectionWidgetOrderedMembers; -use gauntlet_common::model::ListWidget; -use gauntlet_common::model::ListWidgetOrderedMembers; -use gauntlet_common::model::MetadataIconWidget; -use gauntlet_common::model::MetadataLinkWidget; -use gauntlet_common::model::MetadataSeparatorWidget; -use gauntlet_common::model::MetadataTagItemWidget; -use gauntlet_common::model::MetadataTagListWidget; -use gauntlet_common::model::MetadataTagListWidgetOrderedMembers; -use gauntlet_common::model::MetadataValueWidget; -use gauntlet_common::model::MetadataWidget; -use gauntlet_common::model::MetadataWidgetOrderedMembers; -use gauntlet_common::model::ParagraphWidget; -use gauntlet_common::model::PasswordFieldWidget; -use gauntlet_common::model::PhysicalKey; -use gauntlet_common::model::PluginId; use gauntlet_common::model::RootWidget; -use gauntlet_common::model::RootWidgetMembers; -use gauntlet_common::model::SearchBarWidget; -use gauntlet_common::model::SelectItemWidget; -use gauntlet_common::model::SelectWidget; -use gauntlet_common::model::SelectWidgetOrderedMembers; -use gauntlet_common::model::SeparatorWidget; -use gauntlet_common::model::TextAccessoryWidget; -use gauntlet_common::model::TextFieldWidget; -use gauntlet_common::model::UiPropertyValue; -use gauntlet_common::model::UiRenderLocation; -use gauntlet_common::model::UiWidgetId; -use gauntlet_common::model::WidgetVisitor; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use gauntlet_common_plugin_runtime::model::JsUiRenderLocation; use gauntlet_component_model::Component; -use gauntlet_component_model::Component::Root; -use gauntlet_component_model::Property; -use gauntlet_component_model::PropertyType; -use gauntlet_component_model::SharedType; -use indexmap::IndexMap; -use serde::de; -use serde::de::Error; use serde::Deserialize; -use serde::Deserializer; -use serde::Serialize; use tokio::runtime::Handle; use crate::component_model::ComponentModel; diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 690a27a..7863cc0 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -1,9 +1,7 @@ use std::backtrace::Backtrace; use std::fs::File; use std::io::Write; -use std::path::PathBuf; use std::process::exit; -use std::rc::Rc; use std::sync::Arc; use std::thread; use std::time::SystemTime; @@ -14,7 +12,6 @@ use gauntlet_client::start_client; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PluginId; -use gauntlet_common::model::UiTheme; use gauntlet_common::rpc::backend_api::handle_proxy_message; use gauntlet_common::rpc::backend_api::BackendForCliApi; use gauntlet_common::rpc::backend_api::BackendForCliApiProxy; @@ -24,9 +21,6 @@ use gauntlet_common::rpc::backend_api::GrpcBackendApi; use gauntlet_common::rpc::backend_server::start_backend_server; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; -use gauntlet_common::settings_env_data_from_string; -use gauntlet_common::settings_env_data_to_string; -use gauntlet_common::SettingsEnvData; use gauntlet_utils::channel::channel; use gauntlet_utils::channel::RequestError; use gauntlet_utils::channel::RequestReceiver; @@ -35,7 +29,6 @@ use vergen_pretty::vergen_pretty_env; use crate::plugins::ApplicationManager; use crate::rpc::BackendServerImpl; -use crate::search::SearchIndex; pub(crate) mod model; pub mod plugins; @@ -225,7 +218,7 @@ async fn run_server( ) -> anyhow::Result<()> { let application_manager = ApplicationManager::create(frontend_sender).await?; - let mut application_manager = Arc::new(application_manager); + let application_manager = Arc::new(application_manager); application_manager.clear_all_icon_cache_dir()?; diff --git a/rust/server/src/plugins/binary_data_gatherer.rs b/rust/server/src/plugins/binary_data_gatherer.rs index 2c4ee02..59c2828 100644 --- a/rust/server/src/plugins/binary_data_gatherer.rs +++ b/rust/server/src/plugins/binary_data_gatherer.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use std::io::Read; -use futures::StreamExt; use gauntlet_common::model::DataSource; use gauntlet_common::model::DataSourceAsset; use gauntlet_common::model::DataSourceUrl; diff --git a/rust/server/src/plugins/clipboard.rs b/rust/server/src/plugins/clipboard.rs index 3781dbe..503e506 100644 --- a/rust/server/src/plugins/clipboard.rs +++ b/rust/server/src/plugins/clipboard.rs @@ -88,7 +88,7 @@ impl Clipboard { if let Some(png_data) = data.png_data { let cursor = Cursor::new(&png_data); - let mut reader = image::io::Reader::new(cursor); + let mut reader = image::ImageReader::new(cursor); reader.set_format(image::ImageFormat::Png); let image = reader diff --git a/rust/server/src/plugins/config_reader.rs b/rust/server/src/plugins/config_reader.rs index c266491..8fd48f5 100644 --- a/rust/server/src/plugins/config_reader.rs +++ b/rust/server/src/plugins/config_reader.rs @@ -1,24 +1,18 @@ -use std::cell::Cell; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use gauntlet_common::dirs::Dirs; use serde::Deserialize; -use crate::plugins::data_db_repository::DataDbRepository; -use crate::plugins::data_db_repository::DbWritePendingPlugin; - pub struct ConfigReader { dirs: Dirs, - repository: DataDbRepository, close_on_unfocus: AtomicBool, } impl ConfigReader { - pub fn new(dirs: Dirs, repository: DataDbRepository) -> Self { + pub fn new(dirs: Dirs) -> Self { Self { dirs, - repository, close_on_unfocus: AtomicBool::new(true), } } @@ -26,19 +20,6 @@ impl ConfigReader { pub async fn reload_config(&self) -> anyhow::Result<()> { let config = self.read_config(); - // for plugin in config.plugins { - // let exists = self.repository.does_plugin_exist(&plugin.id).await?; - // if !exists { - // let pending = self.repository.is_plugin_pending(&plugin.id).await?; - // if !pending { - // let pending_plugin = DbWritePendingPlugin { - // id: plugin.id - // }; - // self.repository.save_pending_plugin(pending_plugin).await? - // } - // } - // } - self.close_on_unfocus.store( config.main_window.unwrap_or_default().close_on_unfocus, Ordering::SeqCst, @@ -75,10 +56,11 @@ impl ConfigReader { #[derive(Debug, Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct ApplicationConfig { - main_window: Option, // #[serde(default)] - // configuration_mode: ConfigurationModeConfig, - // #[serde(default)] - // plugins: Vec, + main_window: Option, + // #[serde(default)] + // configuration_mode: ConfigurationModeConfig, + // #[serde(default)] + // plugins: Vec, } #[derive(Debug, Deserialize)] diff --git a/rust/server/src/plugins/data_db_repository.rs b/rust/server/src/plugins/data_db_repository.rs index 07b4679..35f17ef 100644 --- a/rust/server/src/plugins/data_db_repository.rs +++ b/rust/server/src/plugins/data_db_repository.rs @@ -1,6 +1,5 @@ use std::collections::HashMap; use std::collections::HashSet; -use std::path::PathBuf; use anyhow::anyhow; use anyhow::Context; @@ -8,29 +7,23 @@ use futures::future::join_all; use futures::StreamExt; use futures::TryStreamExt; use gauntlet_common::dirs::Dirs; -use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalKey; use gauntlet_common::model::PhysicalShortcut; -use gauntlet_common::model::PluginId; -use gauntlet_common::model::UiTheme; use serde::Deserialize; use serde::Serialize; use sqlx::migrate::Migrator; use sqlx::sqlite::SqliteConnectOptions; use sqlx::types::Json; -use sqlx::Error; use sqlx::Executor; use sqlx::Pool; use sqlx::Row; use sqlx::Sqlite; use sqlx::SqlitePool; -use typed_path::TypedPathBuf; use uuid::Uuid; use crate::model::ActionShortcutKey; use crate::plugins::frecency::FrecencyItemStats; use crate::plugins::frecency::FrecencyMetaParams; -use crate::plugins::plugin_manifest::PluginManifestActionShortcutKey; static MIGRATOR: Migrator = sqlx::migrate!("./db_migrations"); @@ -123,6 +116,7 @@ pub enum DbPluginEntrypointType { #[derive(Debug, Clone)] pub enum DbPluginType { Normal, + #[allow(unused)] Config, Bundled, } @@ -384,7 +378,9 @@ pub struct DbWritePendingPlugin { #[derive(sqlx::FromRow)] pub struct DbPluginEntrypointFrecencyStats { + #[allow(unused)] pub plugin_id: String, + #[allow(unused)] pub entrypoint_id: String, pub reference_time: f64, @@ -1296,6 +1292,7 @@ pub fn db_plugin_type_to_str(value: DbPluginType) -> &'static str { } } +#[allow(unused)] pub fn db_plugin_type_from_str(value: &str) -> DbPluginType { match value { "normal" => DbPluginType::Normal, diff --git a/rust/server/src/plugins/frecency.rs b/rust/server/src/plugins/frecency.rs index d62b878..640476d 100644 --- a/rust/server/src/plugins/frecency.rs +++ b/rust/server/src/plugins/frecency.rs @@ -38,6 +38,7 @@ impl FrecencyItemStats { } /// Return the number of half lives passed since the reference time + #[allow(unused)] pub fn half_lives_passed(&self) -> f64 { (current_time_secs() - self.reference_time) / self.half_life } @@ -49,6 +50,7 @@ impl FrecencyItemStats { } /// Change the half life of the item, maintaining the same frecency + #[allow(unused)] pub fn set_half_life(&mut self, half_life: f64) { let secs = current_time_secs(); self.reset_ref_time(secs); @@ -84,6 +86,7 @@ impl FrecencyItemStats { } /// Reset the reference time and recalculate the last_accessed time + #[allow(unused)] pub fn reset_ref_time(&mut self, new_time: f64) { let original_frecency = self.get_frecency(current_time_secs()); let delta = self.reference_time - new_time; @@ -93,6 +96,7 @@ impl FrecencyItemStats { } /// Timestamp (in nanoseconds since epoch) of the last access + #[allow(unused)] pub fn last_access(&self) -> f64 { self.reference_time + self.last_accessed } diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index d46c970..8a4a8d8 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -1,20 +1,9 @@ -use std::cell::RefCell; use std::collections::HashMap; use std::fs::File; -use std::hash::Hash; -use std::io; -use std::net::SocketAddr; -use std::path::Path; -use std::path::PathBuf; -use std::pin::Pin; -use std::rc::Rc; -use std::str::FromStr; use std::sync::Arc; -use std::time::Duration; use anyhow::anyhow; use anyhow::Context; -use futures::AsyncBufReadExt; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::KeyboardEventOrigin; @@ -28,7 +17,6 @@ use gauntlet_common::model::UiRenderLocation; use gauntlet_common::model::UiWidgetId; use gauntlet_common::rpc::frontend_api::FrontendApi; use gauntlet_common::rpc::frontend_api::FrontendApiProxy; -use gauntlet_common::settings_env_data_to_string; use gauntlet_common_plugin_runtime::api::handle_proxy_message; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::model::JsClipboardData; @@ -58,24 +46,15 @@ use interprocess::local_socket::traits::tokio::Listener; use interprocess::local_socket::traits::tokio::Stream; use interprocess::local_socket::ListenerOptions; use interprocess::local_socket::ToFsName; -use interprocess::local_socket::ToNsName; -use interprocess::TryClone; -use once_cell::sync::Lazy; use serde::Deserialize; use serde::Serialize; -use tokio::io::AsyncRead; -use tokio::io::AsyncReadExt; -use tokio::net::TcpStream; use tokio::sync::Mutex; -use tokio::task::spawn_blocking; -use tokio_util::sync::CancellationToken; use crate::model::IntermediateUiEvent; use crate::plugins::binary_data_gatherer::BinaryDataGatherer; use crate::plugins::clipboard::Clipboard; use crate::plugins::data_db_repository::db_entrypoint_from_str; use crate::plugins::data_db_repository::DataDbRepository; -use crate::plugins::data_db_repository::DbPluginClipboardPermissions; use crate::plugins::data_db_repository::DbPluginEntrypointType; use crate::plugins::data_db_repository::DbPluginPreference; use crate::plugins::data_db_repository::DbPluginPreferenceUserData; @@ -176,7 +155,6 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run }; let api = BackendForPluginRuntimeApiImpl::new( - data.icon_cache.clone(), data.db_repository, data.search_index, data.clipboard, @@ -188,7 +166,6 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run ); let mut command_receiver = data.command_receiver; - let cache = data.icon_cache; let plugin_uuid = data.uuid.clone(); let plugin_id = data.id.clone(); @@ -342,7 +319,7 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run }); tokio::select! { - result = { + _ = { let sender = sender.clone(); let plugin_id = plugin_id.clone(); tokio::task::unconstrained(async move { @@ -356,7 +333,7 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run } => { tracing::error!("Event loop has been stopped {:?}", plugin_id) } - result = { + _ = { tokio::task::unconstrained(async { let sender = sender.clone(); loop { @@ -599,11 +576,11 @@ fn from_intermediate_to_js_event(event: IntermediateUiEvent) -> JsEvent { #[derive(Clone)] pub struct BackendForPluginRuntimeApiImpl { - icon_cache: IconCache, repository: DataDbRepository, search_index: SearchIndex, clipboard: Clipboard, frontend_api: FrontendApiProxy, + #[allow(unused)] plugin_uuid: String, plugin_id: PluginId, plugin_name: String, @@ -612,7 +589,6 @@ pub struct BackendForPluginRuntimeApiImpl { impl BackendForPluginRuntimeApiImpl { fn new( - icon_cache: IconCache, repository: DataDbRepository, search_index: SearchIndex, clipboard: Clipboard, @@ -623,7 +599,6 @@ impl BackendForPluginRuntimeApiImpl { permissions: PluginRuntimePermissions, ) -> Self { Self { - icon_cache, repository, search_index, clipboard, diff --git a/rust/server/src/plugins/loader.rs b/rust/server/src/plugins/loader.rs index 61dfa0f..d5a9360 100644 --- a/rust/server/src/plugins/loader.rs +++ b/rust/server/src/plugins/loader.rs @@ -3,7 +3,6 @@ use std::ffi::OsStr; use std::fs::DirEntry; use std::io::ErrorKind; use std::path::Path; -use std::path::PathBuf; use std::thread; use anyhow::anyhow; @@ -13,19 +12,12 @@ use gauntlet_common::model::PluginId; use gauntlet_common_plugin_runtime::PERMISSIONS_VARIABLE_PATTERN; use include_dir::Dir; use itertools::Itertools; -use once_cell::sync::Lazy; -use serde::Deserialize; -use serde::Serialize; -use typed_path::TypedPathBuf; use typed_path::Utf8TypedPath; use typed_path::Utf8UnixComponent; use typed_path::Utf8WindowsComponent; use typed_path::Utf8WindowsPrefix; -use typed_path::Utf8WindowsPrefixComponent; -use uuid::Uuid; use walkdir::WalkDir; -use crate::model::ActionShortcutKey; use crate::plugins::data_db_repository::db_entrypoint_to_str; use crate::plugins::data_db_repository::db_plugin_type_to_str; use crate::plugins::data_db_repository::DataDbRepository; @@ -39,7 +31,6 @@ use crate::plugins::data_db_repository::DbPluginPermissions; use crate::plugins::data_db_repository::DbPluginPermissionsExec; use crate::plugins::data_db_repository::DbPluginPermissionsFileSystem; use crate::plugins::data_db_repository::DbPluginPreference; -use crate::plugins::data_db_repository::DbPluginPreferenceUserData; use crate::plugins::data_db_repository::DbPluginType; use crate::plugins::data_db_repository::DbPreferenceEnumValue; use crate::plugins::data_db_repository::DbWritePlugin; @@ -597,7 +588,6 @@ impl PluginLoader { asset_data, permissions, preferences: plugin_preferences, - preferences_user_data: HashMap::new(), }) } @@ -902,5 +892,4 @@ struct PluginDownloadData { pub asset_data: Vec, pub permissions: DbPluginPermissions, pub preferences: HashMap, - pub preferences_user_data: HashMap, } diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 11f1a8c..22e6667 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -1,12 +1,6 @@ -use std::cell::RefCell; use std::collections::HashMap; -use std::ops::Index; -use std::sync::Mutex; -use std::thread; -use std::time::Duration; use anyhow::anyhow; -use anyhow::Context; use gauntlet_common::detached_process::CommandExt; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::DownloadStatus; @@ -47,20 +41,16 @@ use gauntlet_utils::channel::RequestSender; use include_dir::include_dir; use include_dir::Dir; use itertools::Itertools; -use tokio::runtime::Handle; -use crate::model::ActionShortcutKey; use crate::plugins::clipboard::Clipboard; use crate::plugins::config_reader::ConfigReader; use crate::plugins::data_db_repository::db_entrypoint_from_str; use crate::plugins::data_db_repository::DataDbRepository; -use crate::plugins::data_db_repository::DbPluginActionShortcutKind; use crate::plugins::data_db_repository::DbPluginClipboardPermissions; use crate::plugins::data_db_repository::DbPluginEntrypointType; use crate::plugins::data_db_repository::DbPluginMainSearchBarPermissions; use crate::plugins::data_db_repository::DbPluginPreference; use crate::plugins::data_db_repository::DbPluginPreferenceUserData; -use crate::plugins::data_db_repository::DbReadPluginEntrypoint; use crate::plugins::icon_cache::IconCache; use crate::plugins::js::start_plugin_runtime; use crate::plugins::js::AllPluginCommandData; @@ -120,7 +110,7 @@ impl ApplicationManager { let dirs = Dirs::new(); let db_repository = DataDbRepository::new(dirs.clone()).await?; let plugin_downloader = PluginLoader::new(db_repository.clone()); - let config_reader = ConfigReader::new(dirs.clone(), db_repository.clone()); + let config_reader = ConfigReader::new(dirs.clone()); let icon_cache = IconCache::new(dirs.clone()); let run_status_holder = RunStatusHolder::new(); let clipboard = Clipboard::new()?; @@ -227,7 +217,7 @@ impl ApplicationManager { }; let PluginDataView { - plugin_name, + plugin_name: _, entrypoints, } = data; @@ -236,7 +226,7 @@ impl ApplicationManager { }; let EntrypointDataView { - entrypoint_name, + entrypoint_name: _, entrypoint_generator: _, entrypoint_type, actions, @@ -249,14 +239,7 @@ impl ApplicationManager { self.handle_run_command(plugin_id, entrypoint_id).await; } SearchResultEntrypointType::View => { - self.frontend_api - .open_plugin_view( - plugin_id, - plugin_name.to_string(), - entrypoint_id, - entrypoint_name.to_string(), - ) - .await?; + self.frontend_api.open_plugin_view(plugin_id, entrypoint_id).await?; } SearchResultEntrypointType::Generated => { let Some(action_data) = actions.get(0) else { @@ -271,13 +254,7 @@ impl ApplicationManager { } EntrypointActionType::View => { self.frontend_api - .open_generated_plugin_view( - plugin_id, - plugin_name.to_string(), - entrypoint_id, - entrypoint_name.to_string(), - 0, - ) + .open_generated_plugin_view(plugin_id, entrypoint_id, 0) .await?; } } @@ -305,13 +282,7 @@ impl ApplicationManager { } EntrypointActionType::View => { self.frontend_api - .open_generated_plugin_view( - plugin_id, - plugin_name.to_string(), - entrypoint_id, - entrypoint_name.to_string(), - 1, - ) + .open_generated_plugin_view(plugin_id, entrypoint_id, 1) .await?; } } @@ -349,13 +320,7 @@ impl ApplicationManager { } EntrypointActionType::View => { self.frontend_api - .open_generated_plugin_view( - plugin_id, - plugin_name.to_string(), - entrypoint_id, - entrypoint_name.to_string(), - index, - ) + .open_generated_plugin_view(plugin_id, entrypoint_id, index) .await?; } } @@ -421,9 +386,7 @@ impl ApplicationManager { } } }) - .chunk_by(|(generator_entrypoint_id, entrypoint_id, entrypoint_data)| { - generator_entrypoint_id.clone() - }) + .chunk_by(|(generator_entrypoint_id, _, _)| generator_entrypoint_id.clone()) .into_iter() .map(|(generator_id, data)| { let data: HashMap<_, _> = data diff --git a/rust/server/src/plugins/run_status.rs b/rust/server/src/plugins/run_status.rs index 345cee9..6928da5 100644 --- a/rust/server/src/plugins/run_status.rs +++ b/rust/server/src/plugins/run_status.rs @@ -48,7 +48,7 @@ pub struct RunStatusGuard { impl RunStatusGuard { pub fn stopped(&self) -> WaitForCancellationFutureOwned { - let mut running_plugins = self.running_plugins.lock().expect("lock is poisoned"); + let running_plugins = self.running_plugins.lock().expect("lock is poisoned"); running_plugins .get(&self.id) diff --git a/rust/server/src/plugins/settings.rs b/rust/server/src/plugins/settings.rs index 7384a23..18954af 100644 --- a/rust/server/src/plugins/settings.rs +++ b/rust/server/src/plugins/settings.rs @@ -1,6 +1,4 @@ -use std::collections::hash_map::Entry; use std::collections::HashMap; -use std::rc::Rc; use std::sync::Arc; use anyhow::anyhow; @@ -247,7 +245,7 @@ impl Settings { } pub async fn entrypoint_search_aliases(&self) -> anyhow::Result> { - let mut settings = self.repository.get_settings().await?; + let settings = self.repository.get_settings().await?; let data: HashMap<_, _> = settings .entrypoint_search_aliases @@ -343,7 +341,7 @@ impl Settings { // TODO config - let mut settings = self.repository.get_settings().await?; + let settings = self.repository.get_settings().await?; match settings.theme { None => Ok(SettingsTheme::AutoDetect), @@ -385,7 +383,7 @@ impl Settings { } pub async fn window_position_mode_setting(&self) -> anyhow::Result { - let mut settings = self.repository.get_settings().await?; + let settings = self.repository.get_settings().await?; let window_position_mode = match &settings.window_position_mode { None => WindowPositionMode::Static, diff --git a/rust/server/src/plugins/theme.rs b/rust/server/src/plugins/theme.rs index 8b9ded9..e021b5c 100644 --- a/rust/server/src/plugins/theme.rs +++ b/rust/server/src/plugins/theme.rs @@ -1,11 +1,8 @@ -use std::env::consts::OS; use std::io::ErrorKind; use std::path::PathBuf; use anyhow::anyhow; use anyhow::Context; -use dark_light::Mode; -use gauntlet_common::dirs::Dirs; use gauntlet_common::model::UiTheme; use gauntlet_common::model::UiThemeColor; use gauntlet_common::model::UiThemeContent; @@ -13,12 +10,9 @@ use gauntlet_common::model::UiThemeContentBorder; use gauntlet_common::model::UiThemeMode; use gauntlet_common::model::UiThemeWindow; use gauntlet_common::model::UiThemeWindowBorder; -use gauntlet_common::rpc::frontend_api::FrontendApi; use serde::Deserialize; use serde::Serialize; -use crate::plugins::data_db_repository::DataDbRepository; - pub struct BundledThemes { pub legacy_theme: UiTheme, pub macos_dark_theme: UiTheme, diff --git a/rust/server/src/rpc.rs b/rust/server/src/rpc.rs index 7855da8..635e6d1 100644 --- a/rust/server/src/rpc.rs +++ b/rust/server/src/rpc.rs @@ -1,29 +1,21 @@ use std::collections::HashMap; -use std::rc::Rc; use std::sync::Arc; use gauntlet_common::model::DownloadStatus; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::LocalSaveData; -use gauntlet_common::model::PhysicalKey; use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; use gauntlet_common::model::PluginPreferenceUserData; -use gauntlet_common::model::SearchResult; use gauntlet_common::model::SettingsPlugin; use gauntlet_common::model::SettingsTheme; -use gauntlet_common::model::UiPropertyValue; -use gauntlet_common::model::UiWidgetId; use gauntlet_common::model::WindowPositionMode; use gauntlet_common::rpc::backend_api::BackendForCliApi; use gauntlet_common::rpc::backend_api::BackendForSettingsApi; use gauntlet_common::rpc::backend_api::BackendForToolsApi; -use gauntlet_common::settings_env_data_to_string; -use gauntlet_common::SettingsEnvData; use gauntlet_utils::channel::RequestResult; use crate::plugins::ApplicationManager; -use crate::search::SearchIndex; pub struct BackendServerImpl { pub application_manager: Arc, diff --git a/rust/server/src/search.rs b/rust/server/src/search.rs index 0d71009..a40d9f4 100644 --- a/rust/server/src/search.rs +++ b/rust/server/src/search.rs @@ -1,8 +1,6 @@ -use std::cmp::Ordering; use std::collections::HashMap; use std::sync::Arc; use std::sync::Mutex; -use std::sync::MutexGuard; use anyhow::anyhow; use gauntlet_common::model::EntrypointId; @@ -19,7 +17,6 @@ use tantivy::collector::TopDocs; use tantivy::doc; use tantivy::query::AllQuery; use tantivy::query::BooleanQuery; -use tantivy::query::FuzzyTermQuery; use tantivy::query::Query; use tantivy::query::RegexQuery; use tantivy::query::TermQuery; @@ -27,7 +24,6 @@ use tantivy::schema::*; use tantivy::tokenizer::TokenizerManager; use tantivy::Index; use tantivy::IndexReader; -use tantivy::IndexWriter; use tantivy::ReloadPolicy; use tantivy::Searcher; @@ -335,7 +331,7 @@ impl SearchIndex { self.index_reader.reload()?; if refresh_search_list { - let mut frontend_api = self.frontend_api.clone(); + let frontend_api = self.frontend_api.clone(); tokio::spawn(async move { tracing::info!( "requesting search results update because search index update for plugin: {:?}", diff --git a/rust/utils/src/channel.rs b/rust/utils/src/channel.rs index de538ec..8b082f1 100644 --- a/rust/utils/src/channel.rs +++ b/rust/utils/src/channel.rs @@ -94,7 +94,7 @@ impl RequestSender { let payload = (request, responder); self.request_sender .send(payload) - .map_err(|err| RequestError::OtherSideWasDropped)?; + .map_err(|_err| RequestError::OtherSideWasDropped)?; Ok(ResponseReceiver::new(response_receiver)) } From 80bb33690bb5ad31abb6bf6e13745cebf67d96f4 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 24 May 2025 14:15:12 +0200 Subject: [PATCH 06/91] Run cargo fmt --- rust/cli/src/lib.rs | 4 +-- rust/client/build.rs | 2 +- rust/client/src/global_shortcut.rs | 2 +- rust/client/src/ui/client_context.rs | 2 +- .../src/ui/custom_widgets/loading_bar.rs | 26 +++++++------- rust/client/src/ui/hud/mod.rs | 6 ++-- rust/client/src/ui/mod.rs | 26 +++++++------- rust/client/src/ui/platform/linux.rs | 2 +- rust/client/src/ui/scroll_handle.rs | 4 +-- rust/client/src/ui/search_list.rs | 8 ++--- rust/client/src/ui/state/main_view.rs | 2 +- rust/client/src/ui/state/mod.rs | 6 ++-- rust/client/src/ui/state/plugin_view.rs | 2 +- rust/client/src/ui/sys_tray.rs | 6 ++-- rust/client/src/ui/theme/button.rs | 12 +++---- rust/client/src/ui/theme/checkbox.rs | 8 ++--- rust/client/src/ui/theme/container.rs | 8 ++--- rust/client/src/ui/theme/date_picker.rs | 6 ++-- rust/client/src/ui/theme/grid.rs | 2 +- rust/client/src/ui/theme/loading_bar.rs | 2 +- rust/client/src/ui/theme/mod.rs | 4 +-- rust/client/src/ui/theme/pick_list.rs | 8 ++--- rust/client/src/ui/theme/row.rs | 4 +-- rust/client/src/ui/theme/rule.rs | 2 +- rust/client/src/ui/theme/scrollable.rs | 6 ++-- rust/client/src/ui/theme/text.rs | 6 ++-- rust/client/src/ui/theme/text_input.rs | 10 +++--- rust/client/src/ui/theme/tooltip.rs | 6 ++-- rust/client/src/ui/widget/accessories.rs | 6 ++-- rust/client/src/ui/widget/action_panel.rs | 10 +++--- rust/client/src/ui/widget/content.rs | 4 +-- rust/client/src/ui/widget/data.rs | 6 ++-- rust/client/src/ui/widget/data_mut.rs | 4 +-- rust/client/src/ui/widget/detail.rs | 4 +-- rust/client/src/ui/widget/empty_view.rs | 8 ++--- rust/client/src/ui/widget/events.rs | 2 +- rust/client/src/ui/widget/form.rs | 10 +++--- rust/client/src/ui/widget/grid.rs | 8 ++--- rust/client/src/ui/widget/images.rs | 4 +-- rust/client/src/ui/widget/inline.rs | 8 ++--- rust/client/src/ui/widget/list.rs | 8 ++--- rust/client/src/ui/widget/metadata.rs | 10 +++--- rust/client/src/ui/widget/root.rs | 14 ++++---- rust/client/src/ui/widget/search_bar.rs | 2 +- rust/client/src/ui/widget/state.rs | 2 +- rust/client/src/ui/widget/text.rs | 2 +- rust/client/src/ui/widget_container.rs | 6 ++-- rust/common/build.rs | 2 +- rust/common/src/model.rs | 2 +- rust/common/src/rpc/backend_api.rs | 4 +-- rust/common/src/rpc/backend_server.rs | 10 +++--- rust/common/src/scenario_model.rs | 2 +- rust/common_ui/src/lib.rs | 8 ++--- .../src/components/shortcut_selector.rs | 34 +++++++++---------- rust/management_client/src/theme/button.rs | 18 +++++----- rust/management_client/src/theme/checkbox.rs | 4 +-- rust/management_client/src/theme/container.rs | 6 ++-- .../src/theme/number_input.rs | 2 +- rust/management_client/src/theme/pick_list.rs | 4 +-- rust/management_client/src/theme/rule.rs | 2 +- .../management_client/src/theme/scrollable.rs | 4 +-- .../src/theme/shortcut_selector.rs | 4 +-- rust/management_client/src/theme/table.rs | 4 +-- rust/management_client/src/theme/text.rs | 2 +- .../management_client/src/theme/text_input.rs | 6 ++-- rust/management_client/src/ui.rs | 20 +++++------ rust/management_client/src/views/general.rs | 14 ++++---- rust/management_client/src/views/plugins.rs | 18 +++++----- .../src/views/plugins/preferences.rs | 8 ++--- .../src/views/plugins/table.rs | 18 +++++----- rust/plugin_runtime/src/assets.rs | 2 +- rust/plugin_runtime/src/clipboard.rs | 2 +- rust/plugin_runtime/src/component_model.rs | 2 +- .../src/entrypoint_generators.rs | 2 +- rust/plugin_runtime/src/environment.rs | 2 +- rust/plugin_runtime/src/logs.rs | 2 +- .../src/plugins/applications/macos.rs | 2 +- .../src/plugins/applications/windows.rs | 12 +++---- rust/plugin_runtime/src/plugins/numbat.rs | 6 ++-- rust/plugin_runtime/src/preferences.rs | 2 +- rust/plugin_runtime/src/search.rs | 2 +- rust/server/src/lib.rs | 4 +-- rust/server/src/plugins/clipboard.rs | 2 +- rust/server/src/plugins/data_db_repository.rs | 10 +++--- rust/server/src/plugins/js.rs | 16 ++++----- rust/server/src/plugins/loader.rs | 22 ++++++++---- rust/server/src/plugins/mod.rs | 10 +++--- rust/server/src/plugins/settings.rs | 2 +- rust/server/src/plugins/theme.rs | 2 +- rust/server/src/search.rs | 8 ++--- 90 files changed, 309 insertions(+), 299 deletions(-) diff --git a/rust/cli/src/lib.rs b/rust/cli/src/lib.rs index 888f0aa..0c5bef1 100644 --- a/rust/cli/src/lib.rs +++ b/rust/cli/src/lib.rs @@ -95,8 +95,8 @@ pub fn init() { #[cfg(target_os = "macos")] fn setup_auto_launch_macos() -> anyhow::Result<()> { - use anyhow::anyhow; use anyhow::Context; + use anyhow::anyhow; let app_path = std::env::current_exe().context("Unable to get current_exe from env")?; // expect Gauntlet.app in path according to macos app bundle structure @@ -117,8 +117,8 @@ fn setup_auto_launch_macos() -> anyhow::Result<()> { #[cfg(target_os = "windows")] fn setup_auto_launch_windows() -> anyhow::Result<()> { - use anyhow::anyhow; use anyhow::Context; + use anyhow::anyhow; let app_path = std::env::current_exe() .context("Unable to get current_exe from env")? .as_os_str() diff --git a/rust/client/build.rs b/rust/client/build.rs index 2657559..f14f36b 100644 --- a/rust/client/build.rs +++ b/rust/client/build.rs @@ -5,11 +5,11 @@ use std::path::Path; use convert_case::Case; use convert_case::Casing; -use gauntlet_component_model::create_component_model; use gauntlet_component_model::Component; use gauntlet_component_model::ComponentName; use gauntlet_component_model::Property; use gauntlet_component_model::PropertyType; +use gauntlet_component_model::create_component_model; fn main() -> anyhow::Result<()> { let out_dir = env::var("OUT_DIR")?; diff --git a/rust/client/src/global_shortcut.rs b/rust/client/src/global_shortcut.rs index e0ee697..d02d386 100644 --- a/rust/client/src/global_shortcut.rs +++ b/rust/client/src/global_shortcut.rs @@ -3,8 +3,8 @@ use gauntlet_common::model::PhysicalShortcut; use global_hotkey::hotkey::Code; use global_hotkey::hotkey::HotKey; use global_hotkey::hotkey::Modifiers; -use iced::futures::channel::mpsc::Sender; use iced::futures::SinkExt; +use iced::futures::channel::mpsc::Sender; use tokio::runtime::Handle; use crate::ui::AppMsg; diff --git a/rust/client/src/ui/client_context.rs b/rust/client/src/ui/client_context.rs index 2979d45..5b64535 100644 --- a/rust/client/src/ui/client_context.rs +++ b/rust/client/src/ui/client_context.rs @@ -10,10 +10,10 @@ use gauntlet_common::model::UiWidgetId; use iced::Task; use crate::model::UiViewEvent; +use crate::ui::AppMsg; use crate::ui::widget::action_panel::ActionPanel; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget_container::PluginWidgetContainer; -use crate::ui::AppMsg; pub struct ClientContext { inline_views: Vec<(PluginId, PluginWidgetContainer)>, // Vec to have stable ordering diff --git a/rust/client/src/ui/custom_widgets/loading_bar.rs b/rust/client/src/ui/custom_widgets/loading_bar.rs index 43f04b9..a682a9e 100644 --- a/rust/client/src/ui/custom_widgets/loading_bar.rs +++ b/rust/client/src/ui/custom_widgets/loading_bar.rs @@ -1,19 +1,6 @@ use std::time::Duration; use std::time::Instant; -use iced::advanced::layout::Limits; -use iced::advanced::layout::Node; -use iced::advanced::renderer; -use iced::advanced::widget::tree::State; -use iced::advanced::widget::tree::Tag; -use iced::advanced::widget::Tree; -use iced::advanced::Clipboard; -use iced::advanced::Layout; -use iced::advanced::Shell; -use iced::advanced::Widget; -use iced::event::Status; -use iced::mouse::Cursor; -use iced::window; use iced::Border; use iced::Color; use iced::Element; @@ -22,6 +9,19 @@ use iced::Length; use iced::Rectangle; use iced::Shadow; use iced::Size; +use iced::advanced::Clipboard; +use iced::advanced::Layout; +use iced::advanced::Shell; +use iced::advanced::Widget; +use iced::advanced::layout::Limits; +use iced::advanced::layout::Node; +use iced::advanced::renderer; +use iced::advanced::widget::Tree; +use iced::advanced::widget::tree::State; +use iced::advanced::widget::tree::Tag; +use iced::event::Status; +use iced::mouse::Cursor; +use iced::window; pub struct LoadingBar<'a, Theme> where diff --git a/rust/client/src/ui/hud/mod.rs b/rust/client/src/ui/hud/mod.rs index c5a48f5..71bce95 100644 --- a/rust/client/src/ui/hud/mod.rs +++ b/rust/client/src/ui/hud/mod.rs @@ -1,13 +1,13 @@ use std::convert; use std::time::Duration; +use iced::Point; +use iced::Size; +use iced::Task; use iced::window; use iced::window::Level; use iced::window::Position; use iced::window::Settings; -use iced::Point; -use iced::Size; -use iced::Task; use crate::ui::AppMsg; diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 524aed1..e63b98e 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -34,8 +34,16 @@ use gauntlet_utils::channel::RequestReceiver; use gauntlet_utils::channel::RequestResult; use gauntlet_utils::channel::RequestSender; use gauntlet_utils::channel::Responder; -use global_hotkey::hotkey::HotKey; use global_hotkey::GlobalHotKeyManager; +use global_hotkey::hotkey::HotKey; +use iced::Event; +use iced::Length; +use iced::Point; +use iced::Renderer; +use iced::Settings; +use iced::Size; +use iced::Subscription; +use iced::Task; use iced::advanced::graphics::core::SmolStr; use iced::alignment::Horizontal; use iced::alignment::Vertical; @@ -44,10 +52,10 @@ use iced::font; use iced::futures; use iced::futures::SinkExt; use iced::keyboard; -use iced::keyboard::key::Named; -use iced::keyboard::key::Physical; use iced::keyboard::Key; use iced::keyboard::Modifiers; +use iced::keyboard::key::Named; +use iced::keyboard::key::Physical; use iced::stream; use iced::widget::button; use iced::widget::column; @@ -64,25 +72,17 @@ use iced::window::Level; use iced::window::Mode; use iced::window::Position; use iced::window::Screenshot; -use iced::Event; -use iced::Length; -use iced::Point; -use iced::Renderer; -use iced::Settings; -use iced::Size; -use iced::Subscription; -use iced::Task; use iced_fonts::BOOTSTRAP_FONT_BYTES; use tokio::runtime::Handle; use tokio::sync::RwLock as TokioRwLock; use crate::model::UiViewEvent; use crate::ui::search_list::search_list; +use crate::ui::theme::Element; +use crate::ui::theme::ThemableWidget; use crate::ui::theme::container::ContainerStyle; use crate::ui::theme::container::ContainerStyleInner; use crate::ui::theme::text_input::TextInputStyle; -use crate::ui::theme::Element; -use crate::ui::theme::ThemableWidget; mod client_context; mod custom_widgets; diff --git a/rust/client/src/ui/platform/linux.rs b/rust/client/src/ui/platform/linux.rs index 0948866..49fcb16 100644 --- a/rust/client/src/ui/platform/linux.rs +++ b/rust/client/src/ui/platform/linux.rs @@ -1,8 +1,8 @@ use std::convert::Infallible; use anyhow::anyhow; -use iced::futures::channel::mpsc::Sender; use iced::futures::SinkExt; +use iced::futures::channel::mpsc::Sender; use tokio::runtime::Handle; use x11rb::connection::Connection; use x11rb::properties::WmClass; diff --git a/rust/client/src/ui/scroll_handle.rs b/rust/client/src/ui/scroll_handle.rs index 0e83688..60ef917 100644 --- a/rust/client/src/ui/scroll_handle.rs +++ b/rust/client/src/ui/scroll_handle.rs @@ -1,7 +1,7 @@ -use iced::widget::scrollable::scroll_to; +use iced::Task; use iced::widget::scrollable::AbsoluteOffset; use iced::widget::scrollable::Id; -use iced::Task; +use iced::widget::scrollable::scroll_to; use crate::ui::AppMsg; diff --git a/rust/client/src/ui/search_list.rs b/rust/client/src/ui/search_list.rs index dbc448a..e74b863 100644 --- a/rust/client/src/ui/search_list.rs +++ b/rust/client/src/ui/search_list.rs @@ -6,6 +6,8 @@ use gauntlet_common::model::SearchResult; use gauntlet_common::model::SearchResultAccessory; use gauntlet_common::model::SearchResultEntrypointType; use gauntlet_common::model::TextAccessoryWidget; +use iced::Alignment; +use iced::Length; use iced::advanced::image::Handle; use iced::widget::button; use iced::widget::column; @@ -14,17 +16,15 @@ use iced::widget::horizontal_space; use iced::widget::row; use iced::widget::text; use iced::widget::text::Shaping; -use iced::Alignment; -use iced::Length; use crate::ui::scroll_handle::ScrollHandle; +use crate::ui::theme::Element; +use crate::ui::theme::ThemableWidget; use crate::ui::theme::button::ButtonStyle; use crate::ui::theme::container::ContainerStyle; use crate::ui::theme::image::ImageStyle; use crate::ui::theme::space::ThemeKindSpace; use crate::ui::theme::text::TextStyle; -use crate::ui::theme::Element; -use crate::ui::theme::ThemableWidget; use crate::ui::widget::accessories::render_icon_accessory; use crate::ui::widget::accessories::render_text_accessory; diff --git a/rust/client/src/ui/state/main_view.rs b/rust/client/src/ui/state/main_view.rs index c9c6a2e..9801596 100644 --- a/rust/client/src/ui/state/main_view.rs +++ b/rust/client/src/ui/state/main_view.rs @@ -1,5 +1,5 @@ -use crate::ui::scroll_handle::ScrollHandle; use crate::ui::scroll_handle::ESTIMATED_ACTION_ITEM_HEIGHT; +use crate::ui::scroll_handle::ScrollHandle; pub enum MainViewState { None, diff --git a/rust/client/src/ui/state/mod.rs b/rust/client/src/ui/state/mod.rs index 21a94ee..f311e6a 100644 --- a/rust/client/src/ui/state/mod.rs +++ b/rust/client/src/ui/state/mod.rs @@ -7,16 +7,16 @@ use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; use gauntlet_common::model::SearchResult; +use iced::Task; use iced::widget::text_input; use iced::widget::text_input::focus; -use iced::Task; +use crate::ui::AppMsg; use crate::ui::client_context::ClientContext; -use crate::ui::scroll_handle::ScrollHandle; use crate::ui::scroll_handle::ESTIMATED_MAIN_LIST_ITEM_HEIGHT; +use crate::ui::scroll_handle::ScrollHandle; pub use crate::ui::state::main_view::MainViewState; pub use crate::ui::state::plugin_view::PluginViewState; -use crate::ui::AppMsg; pub enum GlobalState { MainView { diff --git a/rust/client/src/ui/state/plugin_view.rs b/rust/client/src/ui/state/plugin_view.rs index f3627b2..995d157 100644 --- a/rust/client/src/ui/state/plugin_view.rs +++ b/rust/client/src/ui/state/plugin_view.rs @@ -1,5 +1,5 @@ -use crate::ui::scroll_handle::ScrollHandle; use crate::ui::scroll_handle::ESTIMATED_ACTION_ITEM_HEIGHT; +use crate::ui::scroll_handle::ScrollHandle; #[derive(Debug, Clone)] pub enum PluginViewState { diff --git a/rust/client/src/ui/sys_tray.rs b/rust/client/src/ui/sys_tray.rs index ccd4f5e..cd140a5 100644 --- a/rust/client/src/ui/sys_tray.rs +++ b/rust/client/src/ui/sys_tray.rs @@ -1,15 +1,15 @@ use image::ImageFormat; pub fn create_tray() -> tray_icon::TrayIcon { - use global_hotkey::hotkey::Code; use global_hotkey::hotkey::CMD_OR_CTRL; - use tray_icon::menu::accelerator::Accelerator; + use global_hotkey::hotkey::Code; + use tray_icon::TrayIconBuilder; use tray_icon::menu::AboutMetadataBuilder; use tray_icon::menu::Menu; use tray_icon::menu::MenuEvent; use tray_icon::menu::MenuItem; use tray_icon::menu::PredefinedMenuItem; - use tray_icon::TrayIconBuilder; + use tray_icon::menu::accelerator::Accelerator; MenuEvent::set_event_handler(Some(|event: MenuEvent| { match event.id().as_ref() { diff --git a/rust/client/src/ui/theme/button.rs b/rust/client/src/ui/theme/button.rs index e00d6ae..25501f1 100644 --- a/rust/client/src/ui/theme/button.rs +++ b/rust/client/src/ui/theme/button.rs @@ -1,18 +1,18 @@ use button::Style; -use iced::widget::button; -use iced::widget::button::Status; -use iced::widget::Button; use iced::Border; use iced::Color; use iced::Padding; use iced::Renderer; +use iced::widget::Button; +use iced::widget::button; +use iced::widget::button::Status; -use crate::ui::theme::get_theme; -use crate::ui::theme::padding_all; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; -use crate::ui::theme::ThemableWidget; use crate::ui::theme::NOT_INTENDED_TO_BE_USED; +use crate::ui::theme::ThemableWidget; +use crate::ui::theme::get_theme; +use crate::ui::theme::padding_all; #[derive(Debug, Clone, Copy)] pub enum ButtonStyle { diff --git a/rust/client/src/ui/theme/checkbox.rs b/rust/client/src/ui/theme/checkbox.rs index 3d9f861..2007102 100644 --- a/rust/client/src/ui/theme/checkbox.rs +++ b/rust/client/src/ui/theme/checkbox.rs @@ -1,14 +1,14 @@ +use iced::Border; +use iced::Renderer; +use iced::widget::Checkbox; use iced::widget::checkbox; use iced::widget::checkbox::Status; use iced::widget::checkbox::Style; -use iced::widget::Checkbox; -use iced::Border; -use iced::Renderer; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; -use crate::ui::theme::ThemableWidget; use crate::ui::theme::NOT_INTENDED_TO_BE_USED; +use crate::ui::theme::ThemableWidget; pub enum CheckboxStyle { Default, diff --git a/rust/client/src/ui/theme/container.rs b/rust/client/src/ui/theme/container.rs index 5cc7971..34104a1 100644 --- a/rust/client/src/ui/theme/container.rs +++ b/rust/client/src/ui/theme/container.rs @@ -1,17 +1,17 @@ -use iced::widget::container; -use iced::widget::container::Style; -use iced::widget::Container; use iced::Border; use iced::Color; use iced::Length; use iced::Renderer; use iced::Shadow; use iced::Vector; +use iced::widget::Container; +use iced::widget::container; +use iced::widget::container::Style; -use crate::ui::theme::get_theme; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; use crate::ui::theme::ThemableWidget; +use crate::ui::theme::get_theme; pub enum ContainerStyle { ActionPanel, diff --git a/rust/client/src/ui/theme/date_picker.rs b/rust/client/src/ui/theme/date_picker.rs index 89ed158..b1aad92 100644 --- a/rust/client/src/ui/theme/date_picker.rs +++ b/rust/client/src/ui/theme/date_picker.rs @@ -1,8 +1,8 @@ use iced::Color; -use iced_aw::date_picker::Style; -use iced_aw::style::date_picker::Catalog; -use iced_aw::style::Status; use iced_aw::DatePicker; +use iced_aw::date_picker::Style; +use iced_aw::style::Status; +use iced_aw::style::date_picker::Catalog; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; diff --git a/rust/client/src/ui/theme/grid.rs b/rust/client/src/ui/theme/grid.rs index 75215cd..b8e6961 100644 --- a/rust/client/src/ui/theme/grid.rs +++ b/rust/client/src/ui/theme/grid.rs @@ -1,10 +1,10 @@ use iced::Renderer; use iced_aw::Grid; -use crate::ui::theme::get_theme; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; use crate::ui::theme::ThemableWidget; +use crate::ui::theme::get_theme; pub enum GridStyle { Default, diff --git a/rust/client/src/ui/theme/loading_bar.rs b/rust/client/src/ui/theme/loading_bar.rs index 08bd4d0..ede0aed 100644 --- a/rust/client/src/ui/theme/loading_bar.rs +++ b/rust/client/src/ui/theme/loading_bar.rs @@ -1,9 +1,9 @@ +use crate::ui::GauntletComplexTheme; use crate::ui::custom_widgets::loading_bar; use crate::ui::custom_widgets::loading_bar::LoadingBar; use crate::ui::custom_widgets::loading_bar::Style; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; -use crate::ui::GauntletComplexTheme; #[derive(Default)] pub enum LoadingBarStyle { diff --git a/rust/client/src/ui/theme/mod.rs b/rust/client/src/ui/theme/mod.rs index 28a4414..7584e34 100644 --- a/rust/client/src/ui/theme/mod.rs +++ b/rust/client/src/ui/theme/mod.rs @@ -5,10 +5,10 @@ use arc_swap::Guard; use gauntlet_common::model::UiTheme; use gauntlet_common::model::UiThemeColor; use gauntlet_common::model::UiThemeMode; -use iced::application; -use iced::application::DefaultStyle; use iced::Color; use iced::Padding; +use iced::application; +use iced::application::DefaultStyle; pub mod button; pub mod checkbox; diff --git a/rust/client/src/ui/theme/pick_list.rs b/rust/client/src/ui/theme/pick_list.rs index f1dfa51..6262210 100644 --- a/rust/client/src/ui/theme/pick_list.rs +++ b/rust/client/src/ui/theme/pick_list.rs @@ -1,16 +1,16 @@ use std::borrow::Borrow; +use iced::Border; use iced::overlay; +use iced::widget::PickList; use iced::widget::pick_list; use iced::widget::pick_list::Status; -use iced::widget::PickList; -use iced::Border; -use crate::ui::theme::get_theme; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; -use crate::ui::theme::ThemableWidget; use crate::ui::theme::NOT_INTENDED_TO_BE_USED; +use crate::ui::theme::ThemableWidget; +use crate::ui::theme::get_theme; #[derive(Clone, Default)] pub enum PickListStyle { diff --git a/rust/client/src/ui/theme/row.rs b/rust/client/src/ui/theme/row.rs index d85e404..16acc51 100644 --- a/rust/client/src/ui/theme/row.rs +++ b/rust/client/src/ui/theme/row.rs @@ -1,10 +1,10 @@ -use iced::widget::Row; use iced::Renderer; +use iced::widget::Row; -use crate::ui::theme::get_theme; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; use crate::ui::theme::ThemableWidget; +use crate::ui::theme::get_theme; pub enum RowStyle { ActionShortcut, diff --git a/rust/client/src/ui/theme/rule.rs b/rust/client/src/ui/theme/rule.rs index a880837..9031970 100644 --- a/rust/client/src/ui/theme/rule.rs +++ b/rust/client/src/ui/theme/rule.rs @@ -1,6 +1,6 @@ +use iced::widget::Rule; use iced::widget::rule; use iced::widget::rule::Style; -use iced::widget::Rule; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; diff --git a/rust/client/src/ui/theme/scrollable.rs b/rust/client/src/ui/theme/scrollable.rs index 4d736c9..da755bb 100644 --- a/rust/client/src/ui/theme/scrollable.rs +++ b/rust/client/src/ui/theme/scrollable.rs @@ -1,12 +1,12 @@ +use iced::Border; +use iced::Color; use iced::widget::container; use iced::widget::scrollable; use iced::widget::scrollable::Status; use iced::widget::scrollable::Style; -use iced::Border; -use iced::Color; -use crate::ui::theme::get_theme; use crate::ui::theme::GauntletComplexTheme; +use crate::ui::theme::get_theme; impl scrollable::Catalog for GauntletComplexTheme { type Class<'a> = (); diff --git a/rust/client/src/ui/theme/text.rs b/rust/client/src/ui/theme/text.rs index 13be5d2..dab07bf 100644 --- a/rust/client/src/ui/theme/text.rs +++ b/rust/client/src/ui/theme/text.rs @@ -1,12 +1,12 @@ +use iced::Renderer; +use iced::widget::Text; use iced::widget::text; use iced::widget::text::Style; -use iced::widget::Text; -use iced::Renderer; -use crate::ui::theme::get_theme; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; use crate::ui::theme::ThemableWidget; +use crate::ui::theme::get_theme; #[derive(Clone, Default)] pub enum TextStyle { diff --git a/rust/client/src/ui/theme/text_input.rs b/rust/client/src/ui/theme/text_input.rs index 250105e..1fcd8b8 100644 --- a/rust/client/src/ui/theme/text_input.rs +++ b/rust/client/src/ui/theme/text_input.rs @@ -1,15 +1,15 @@ -use iced::widget::text_input; -use iced::widget::text_input::Status; -use iced::widget::text_input::Style; -use iced::widget::TextInput; use iced::Border; use iced::Color; use iced::Renderer; +use iced::widget::TextInput; +use iced::widget::text_input; +use iced::widget::text_input::Status; +use iced::widget::text_input::Style; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; -use crate::ui::theme::ThemableWidget; use crate::ui::theme::NOT_INTENDED_TO_BE_USED; +use crate::ui::theme::ThemableWidget; pub enum TextInputStyle { ShouldNotBeUsed, diff --git a/rust/client/src/ui/theme/tooltip.rs b/rust/client/src/ui/theme/tooltip.rs index 0551a4e..555968e 100644 --- a/rust/client/src/ui/theme/tooltip.rs +++ b/rust/client/src/ui/theme/tooltip.rs @@ -1,11 +1,11 @@ -use iced::widget::Tooltip; use iced::Renderer; +use iced::widget::Tooltip; -use crate::ui::theme::container::ContainerStyleInner; -use crate::ui::theme::get_theme; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; use crate::ui::theme::ThemableWidget; +use crate::ui::theme::container::ContainerStyleInner; +use crate::ui::theme::get_theme; pub enum TooltipStyle { Tooltip, diff --git a/rust/client/src/ui/widget/accessories.rs b/rust/client/src/ui/widget/accessories.rs index bee15e6..cccfce9 100644 --- a/rust/client/src/ui/widget/accessories.rs +++ b/rust/client/src/ui/widget/accessories.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use gauntlet_common::model::IconAccessoryWidget; use gauntlet_common::model::TextAccessoryWidget; use gauntlet_common::model::UiWidgetId; +use iced::Alignment; use iced::advanced::text::Shaping; use iced::alignment::Horizontal; use iced::alignment::Vertical; @@ -11,13 +12,12 @@ use iced::widget::row; use iced::widget::text; use iced::widget::tooltip; use iced::widget::tooltip::Position; -use iced::Alignment; +use crate::ui::theme::Element; +use crate::ui::theme::ThemableWidget; use crate::ui::theme::container::ContainerStyle; use crate::ui::theme::text::TextStyle; use crate::ui::theme::tooltip::TooltipStyle; -use crate::ui::theme::Element; -use crate::ui::theme::ThemableWidget; use crate::ui::widget::images::render_image; pub fn render_icon_accessory<'a, T: 'a + Clone>( diff --git a/rust/client/src/ui/widget/action_panel.rs b/rust/client/src/ui/widget/action_panel.rs index 996a2cf..4e64ac9 100644 --- a/rust/client/src/ui/widget/action_panel.rs +++ b/rust/client/src/ui/widget/action_panel.rs @@ -10,6 +10,9 @@ use gauntlet_common::model::PhysicalKey; use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::UiWidgetId; use gauntlet_common_ui::shortcut_to_text; +use iced::Alignment; +use iced::Font; +use iced::Length; use iced::advanced::text::Shaping; use iced::font::Weight; use iced::widget::button; @@ -20,18 +23,15 @@ use iced::widget::horizontal_space; use iced::widget::row; use iced::widget::scrollable; use iced::widget::text; -use iced::Alignment; -use iced::Font; -use iced::Length; use crate::ui::scroll_handle::ScrollHandle; +use crate::ui::theme::Element; +use crate::ui::theme::ThemableWidget; use crate::ui::theme::button::ButtonStyle; use crate::ui::theme::container::ContainerStyle; use crate::ui::theme::row::RowStyle; use crate::ui::theme::rule::RuleStyle; use crate::ui::theme::text::TextStyle; -use crate::ui::theme::Element; -use crate::ui::theme::ThemableWidget; #[derive(Debug)] pub struct ActionPanel { diff --git a/rust/client/src/ui/widget/content.rs b/rust/client/src/ui/widget/content.rs index 9a02dca..da50374 100644 --- a/rust/client/src/ui/widget/content.rs +++ b/rust/client/src/ui/widget/content.rs @@ -11,16 +11,16 @@ use gauntlet_common::model::HorizontalBreakWidget; use gauntlet_common::model::ImageWidget; use gauntlet_common::model::ParagraphWidget; use gauntlet_common::model::SvgWidget; +use iced::Length; use iced::alignment::Horizontal; use iced::alignment::Vertical; use iced::widget::column; use iced::widget::container; use iced::widget::horizontal_rule; -use iced::Length; -use crate::ui::theme::container::ContainerStyle; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; +use crate::ui::theme::container::ContainerStyle; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::images::render_image; diff --git a/rust/client/src/ui/widget/data.rs b/rust/client/src/ui/widget/data.rs index 2dd6d09..3d2e247 100644 --- a/rust/client/src/ui/widget/data.rs +++ b/rust/client/src/ui/widget/data.rs @@ -15,13 +15,14 @@ use gauntlet_common::model::RootWidget; use gauntlet_common::model::RootWidgetMembers; use gauntlet_common::model::UiRenderLocation; use gauntlet_common::model::UiWidgetId; -use iced::widget::text_input; use iced::Task; +use iced::widget::text_input; +use crate::ui::AppMsg; use crate::ui::grid_navigation::GridSectionData; use crate::ui::scroll_handle::ScrollHandle; -use crate::ui::widget::action_panel::convert_action_panel; use crate::ui::widget::action_panel::ActionPanel; +use crate::ui::widget::action_panel::convert_action_panel; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::grid::grid_width; use crate::ui::widget::state::CheckboxState; @@ -30,7 +31,6 @@ use crate::ui::widget::state::DatePickerState; use crate::ui::widget::state::RootState; use crate::ui::widget::state::SelectState; use crate::ui::widget::state::TextFieldState; -use crate::ui::AppMsg; #[derive(Debug)] pub struct ComponentWidgets<'b> { diff --git a/rust/client/src/ui/widget/data_mut.rs b/rust/client/src/ui/widget/data_mut.rs index 16a5337..416b9fa 100644 --- a/rust/client/src/ui/widget/data_mut.rs +++ b/rust/client/src/ui/widget/data_mut.rs @@ -9,16 +9,16 @@ use gauntlet_common::model::PluginId; use gauntlet_common::model::RootWidget; use gauntlet_common::model::RootWidgetMembers; use gauntlet_common::model::UiWidgetId; -use iced::widget::text_input; use iced::Task; +use iced::widget::text_input; +use crate::ui::AppMsg; use crate::ui::grid_navigation::grid_down_offset; use crate::ui::grid_navigation::grid_up_offset; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::state::ComponentWidgetState; use crate::ui::widget::state::RootState; use crate::ui::widget::state::TextFieldState; -use crate::ui::AppMsg; #[derive(Debug)] pub struct ComponentWidgetsMut<'b> { diff --git a/rust/client/src/ui/widget/detail.rs b/rust/client/src/ui/widget/detail.rs index 20d4a07..5c7e054 100644 --- a/rust/client/src/ui/widget/detail.rs +++ b/rust/client/src/ui/widget/detail.rs @@ -1,15 +1,15 @@ use gauntlet_common::model::DetailWidget; +use iced::Length; use iced::widget::column; use iced::widget::container; use iced::widget::horizontal_rule; use iced::widget::row; use iced::widget::scrollable; use iced::widget::vertical_rule; -use iced::Length; -use crate::ui::theme::container::ContainerStyle; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; +use crate::ui::theme::container::ContainerStyle; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; diff --git a/rust/client/src/ui/widget/empty_view.rs b/rust/client/src/ui/widget/empty_view.rs index c61b226..c52af88 100644 --- a/rust/client/src/ui/widget/empty_view.rs +++ b/rust/client/src/ui/widget/empty_view.rs @@ -1,4 +1,6 @@ use gauntlet_common::model::EmptyViewWidget; +use iced::Alignment; +use iced::Length; use iced::advanced::text::Shaping; use iced::alignment::Horizontal; use iced::alignment::Vertical; @@ -6,13 +8,11 @@ use iced::widget::column; use iced::widget::container; use iced::widget::horizontal_space; use iced::widget::text; -use iced::Alignment; -use iced::Length; -use crate::ui::theme::container::ContainerStyle; -use crate::ui::theme::text::TextStyle; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; +use crate::ui::theme::container::ContainerStyle; +use crate::ui::theme::text::TextStyle; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::images::render_image; diff --git a/rust/client/src/ui/widget/events.rs b/rust/client/src/ui/widget/events.rs index ed250b4..2800130 100644 --- a/rust/client/src/ui/widget/events.rs +++ b/rust/client/src/ui/widget/events.rs @@ -2,12 +2,12 @@ use gauntlet_common::model::PluginId; use gauntlet_common::model::UiWidgetId; use crate::model::UiViewEvent; +use crate::ui::AppMsg; use crate::ui::widget::state::CheckboxState; use crate::ui::widget::state::ComponentWidgetState; use crate::ui::widget::state::DatePickerState; use crate::ui::widget::state::SelectState; use crate::ui::widget::state::TextFieldState; -use crate::ui::AppMsg; include!(concat!(env!("OUT_DIR"), "/components.rs")); diff --git a/rust/client/src/ui/widget/form.rs b/rust/client/src/ui/widget/form.rs index a6f372e..a661f6e 100644 --- a/rust/client/src/ui/widget/form.rs +++ b/rust/client/src/ui/widget/form.rs @@ -11,8 +11,11 @@ use gauntlet_common::model::SelectWidget; use gauntlet_common::model::SelectWidgetOrderedMembers; use gauntlet_common::model::SeparatorWidget; use gauntlet_common::model::TextFieldWidget; +use iced::Alignment; +use iced::Length; use iced::advanced::text::Shaping; use iced::alignment::Horizontal; +use iced::widget::Space; use iced::widget::button; use iced::widget::checkbox; use iced::widget::column; @@ -23,19 +26,16 @@ use iced::widget::row; use iced::widget::scrollable; use iced::widget::text; use iced::widget::text_input; -use iced::widget::Space; -use iced::Alignment; -use iced::Length; use iced_aw::date_picker; use crate::ui::state::PluginViewState; +use crate::ui::theme::Element; +use crate::ui::theme::ThemableWidget; use crate::ui::theme::container::ContainerStyle; use crate::ui::theme::date_picker::DatePickerStyle; use crate::ui::theme::pick_list::PickListStyle; use crate::ui::theme::row::RowStyle; use crate::ui::theme::text_input::TextInputStyle; -use crate::ui::theme::Element; -use crate::ui::theme::ThemableWidget; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::state::CheckboxState; diff --git a/rust/client/src/ui/widget/grid.rs b/rust/client/src/ui/widget/grid.rs index 155d2ca..bb458ab 100644 --- a/rust/client/src/ui/widget/grid.rs +++ b/rust/client/src/ui/widget/grid.rs @@ -7,6 +7,7 @@ use gauntlet_common::model::GridSectionWidgetOrderedMembers; use gauntlet_common::model::GridWidget; use gauntlet_common::model::GridWidgetOrderedMembers; use gauntlet_common::model::PhysicalShortcut; +use iced::Length; use iced::advanced::text::Shaping; use iced::alignment::Vertical; use iced::widget::button; @@ -16,20 +17,19 @@ use iced::widget::horizontal_space; use iced::widget::row; use iced::widget::scrollable; use iced::widget::text; -use iced::Length; +use iced_aw::GridRow; use iced_aw::grid; use iced_aw::grid_row; -use iced_aw::GridRow; use itertools::Itertools; use crate::ui::state::PluginViewState; +use crate::ui::theme::Element; +use crate::ui::theme::ThemableWidget; use crate::ui::theme::button::ButtonStyle; use crate::ui::theme::container::ContainerStyle; use crate::ui::theme::grid::GridStyle; use crate::ui::theme::row::RowStyle; use crate::ui::theme::text::TextStyle; -use crate::ui::theme::Element; -use crate::ui::theme::ThemableWidget; use crate::ui::widget::accessories::render_icon_accessory; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; diff --git a/rust/client/src/ui/widget/images.rs b/rust/client/src/ui/widget/images.rs index bb8da8c..7571126 100644 --- a/rust/client/src/ui/widget/images.rs +++ b/rust/client/src/ui/widget/images.rs @@ -7,12 +7,12 @@ use iced::widget::horizontal_space; use iced::widget::image; use iced::widget::svg; use iced::widget::value; -use iced_fonts::Bootstrap; use iced_fonts::BOOTSTRAP_FONT; +use iced_fonts::Bootstrap; -use crate::ui::theme::text::TextStyle; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; +use crate::ui::theme::text::TextStyle; pub fn render_image<'a, T: 'a + Clone>( data: &HashMap>, diff --git a/rust/client/src/ui/widget/inline.rs b/rust/client/src/ui/widget/inline.rs index 3c9e45a..d674e93 100644 --- a/rust/client/src/ui/widget/inline.rs +++ b/rust/client/src/ui/widget/inline.rs @@ -1,6 +1,8 @@ use gauntlet_common::model::InlineSeparatorWidget; use gauntlet_common::model::InlineWidget; use gauntlet_common::model::InlineWidgetOrderedMembers; +use iced::Alignment; +use iced::Length; use iced::advanced::text::Shaping; use iced::alignment::Horizontal; use iced::widget::column; @@ -9,14 +11,12 @@ use iced::widget::row; use iced::widget::text; use iced::widget::value; use iced::widget::vertical_rule; -use iced::Alignment; -use iced::Length; use iced_fonts::BOOTSTRAP_FONT; -use crate::ui::theme::container::ContainerStyle; -use crate::ui::theme::text::TextStyle; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; +use crate::ui::theme::container::ContainerStyle; +use crate::ui::theme::text::TextStyle; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::images::icon_to_bootstrap; diff --git a/rust/client/src/ui/widget/list.rs b/rust/client/src/ui/widget/list.rs index 8612da9..5769067 100644 --- a/rust/client/src/ui/widget/list.rs +++ b/rust/client/src/ui/widget/list.rs @@ -8,6 +8,8 @@ use gauntlet_common::model::ListSectionWidgetOrderedMembers; use gauntlet_common::model::ListWidget; use gauntlet_common::model::ListWidgetOrderedMembers; use gauntlet_common::model::PhysicalShortcut; +use iced::Alignment; +use iced::Length; use iced::advanced::text::Shaping; use iced::widget::button; use iced::widget::column; @@ -17,16 +19,14 @@ use iced::widget::row; use iced::widget::scrollable; use iced::widget::text; use iced::widget::vertical_rule; -use iced::Alignment; -use iced::Length; use crate::ui::state::PluginViewState; +use crate::ui::theme::Element; +use crate::ui::theme::ThemableWidget; use crate::ui::theme::button::ButtonStyle; use crate::ui::theme::container::ContainerStyle; use crate::ui::theme::row::RowStyle; use crate::ui::theme::text::TextStyle; -use crate::ui::theme::Element; -use crate::ui::theme::ThemableWidget; use crate::ui::widget::accessories::render_icon_accessory; use crate::ui::widget::accessories::render_text_accessory; use crate::ui::widget::data::ComponentWidgets; diff --git a/rust/client/src/ui/widget/metadata.rs b/rust/client/src/ui/widget/metadata.rs index 48a4ea4..b26d463 100644 --- a/rust/client/src/ui/widget/metadata.rs +++ b/rust/client/src/ui/widget/metadata.rs @@ -7,6 +7,8 @@ use gauntlet_common::model::MetadataTagListWidgetOrderedMembers; use gauntlet_common::model::MetadataValueWidget; use gauntlet_common::model::MetadataWidget; use gauntlet_common::model::MetadataWidgetOrderedMembers; +use iced::Alignment; +use iced::Length; use iced::advanced::text::Shaping; use iced::widget::button; use iced::widget::column; @@ -19,17 +21,15 @@ use iced::widget::text; use iced::widget::tooltip; use iced::widget::tooltip::Position; use iced::widget::value; -use iced::Alignment; -use iced::Length; -use iced_fonts::Bootstrap; use iced_fonts::BOOTSTRAP_FONT; +use iced_fonts::Bootstrap; +use crate::ui::theme::Element; +use crate::ui::theme::ThemableWidget; use crate::ui::theme::button::ButtonStyle; use crate::ui::theme::container::ContainerStyle; use crate::ui::theme::text::TextStyle; use crate::ui::theme::tooltip::TooltipStyle; -use crate::ui::theme::Element; -use crate::ui::theme::ThemableWidget; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::images::icon_to_bootstrap; diff --git a/rust/client/src/ui/widget/root.rs b/rust/client/src/ui/widget/root.rs index 6c84fb8..9004047 100644 --- a/rust/client/src/ui/widget/root.rs +++ b/rust/client/src/ui/widget/root.rs @@ -6,7 +6,10 @@ use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::RootWidgetMembers; use gauntlet_common::model::SearchBarWidget; use gauntlet_common::model::UiWidgetId; +use iced::Alignment; +use iced::Length; use iced::advanced::text::Shaping; +use iced::widget::Space; use iced::widget::button; use iced::widget::column; use iced::widget::container; @@ -18,26 +21,23 @@ use iced::widget::stack; use iced::widget::text; use iced::widget::value; use iced::widget::vertical_rule; -use iced::widget::Space; -use iced::Alignment; -use iced::Length; -use iced_fonts::Bootstrap; use iced_fonts::BOOTSTRAP_FONT; +use iced_fonts::Bootstrap; use crate::ui::custom_widgets::loading_bar::LoadingBar; use crate::ui::scroll_handle::ScrollHandle; use crate::ui::state::PluginViewState; +use crate::ui::theme::Element; +use crate::ui::theme::ThemableWidget; use crate::ui::theme::button::ButtonStyle; use crate::ui::theme::container::ContainerStyle; use crate::ui::theme::row::RowStyle; use crate::ui::theme::rule::RuleStyle; use crate::ui::theme::text::TextStyle; -use crate::ui::theme::Element; -use crate::ui::theme::ThemableWidget; +use crate::ui::widget::action_panel::ActionPanel; use crate::ui::widget::action_panel::convert_action_panel; use crate::ui::widget::action_panel::render_action_panel; use crate::ui::widget::action_panel::render_shortcut; -use crate::ui::widget::action_panel::ActionPanel; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::state::RootState; diff --git a/rust/client/src/ui/widget/search_bar.rs b/rust/client/src/ui/widget/search_bar.rs index 95543ef..a133777 100644 --- a/rust/client/src/ui/widget/search_bar.rs +++ b/rust/client/src/ui/widget/search_bar.rs @@ -1,9 +1,9 @@ use gauntlet_common::model::SearchBarWidget; use iced::widget::text_input; -use crate::ui::theme::text_input::TextInputStyle; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; +use crate::ui::theme::text_input::TextInputStyle; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::state::TextFieldState; diff --git a/rust/client/src/ui/widget/state.rs b/rust/client/src/ui/widget/state.rs index b3659cb..d817ead 100644 --- a/rust/client/src/ui/widget/state.rs +++ b/rust/client/src/ui/widget/state.rs @@ -9,8 +9,8 @@ use gauntlet_common::model::UiWidgetId; use iced::widget::text_input; use iced_aw::date_picker::Date; -use crate::ui::scroll_handle::ScrollHandle; use crate::ui::scroll_handle::ESTIMATED_MAIN_LIST_ITEM_HEIGHT; +use crate::ui::scroll_handle::ScrollHandle; use crate::ui::widget::grid::grid_width; pub fn create_state(root_widget: &RootWidget) -> HashMap { diff --git a/rust/client/src/ui/widget/text.rs b/rust/client/src/ui/widget/text.rs index d47734c..fdab4b0 100644 --- a/rust/client/src/ui/widget/text.rs +++ b/rust/client/src/ui/widget/text.rs @@ -1,7 +1,7 @@ +use iced::Font; use iced::advanced::text::Shaping; use iced::font::Weight; use iced::widget::text; -use iced::Font; use crate::ui::theme::Element; use crate::ui::widget::data::ComponentWidgets; diff --git a/rust/client/src/ui/widget_container.rs b/rust/client/src/ui/widget_container.rs index ce65b26..17a8e29 100644 --- a/rust/client/src/ui/widget_container.rs +++ b/rust/client/src/ui/widget_container.rs @@ -1,5 +1,5 @@ -use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::mem; use std::sync::Arc; @@ -11,15 +11,15 @@ use gauntlet_common::model::UiWidgetId; use iced::Task; use crate::model::UiViewEvent; +use crate::ui::AppMsg; use crate::ui::state::PluginViewState; use crate::ui::theme::Element; use crate::ui::widget::action_panel::ActionPanel; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::data_mut::ComponentWidgetsMut; use crate::ui::widget::events::ComponentWidgetEvent; -use crate::ui::widget::state::create_state; use crate::ui::widget::state::ComponentWidgetState; -use crate::ui::AppMsg; +use crate::ui::widget::state::create_state; pub struct PluginWidgetContainer { root_widget: Option>, diff --git a/rust/common/build.rs b/rust/common/build.rs index 70cc682..7017eec 100644 --- a/rust/common/build.rs +++ b/rust/common/build.rs @@ -6,7 +6,6 @@ use std::path::Path; use convert_case::Case; use convert_case::Casing; -use gauntlet_component_model::create_component_model; use gauntlet_component_model::Arity; use gauntlet_component_model::Children; use gauntlet_component_model::Component; @@ -16,6 +15,7 @@ use gauntlet_component_model::Property; use gauntlet_component_model::PropertyKind; use gauntlet_component_model::PropertyType; use gauntlet_component_model::SharedType; +use gauntlet_component_model::create_component_model; use indexmap::IndexMap; use itertools::Itertools; diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index 145fc8f..5336fd3 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -9,11 +9,11 @@ use bincode::Decode; use bincode::Encode; use gix_url::Scheme; use gix_url::Url; -use serde::de::Error; use serde::Deserialize; use serde::Deserializer; use serde::Serialize; use serde::Serializer; +use serde::de::Error; #[derive(Debug, Clone, PartialEq, Eq, Hash, Encode, Decode)] pub struct PluginId(Arc); diff --git a/rust/common/src/rpc/backend_api.rs b/rust/common/src/rpc/backend_api.rs index 567c858..ec01eb2 100644 --- a/rust/common/src/rpc/backend_api.rs +++ b/rust/common/src/rpc/backend_api.rs @@ -4,8 +4,8 @@ use std::sync::Arc; use gauntlet_utils::channel::RequestResult; use gauntlet_utils_macros::boundary_gen; use tokio::sync::Mutex; -use tonic::transport::Channel; use tonic::Request; +use tonic::transport::Channel; use crate::model::DownloadStatus; use crate::model::EntrypointId; @@ -22,9 +22,9 @@ use crate::model::UiPropertyValue; use crate::model::UiSetupData; use crate::model::UiWidgetId; use crate::model::WindowPositionMode; -use crate::rpc::grpc::rpc_backend_client::RpcBackendClient; use crate::rpc::grpc::RpcBincode; use crate::rpc::grpc::RpcSaveLocalPluginRequest; +use crate::rpc::grpc::rpc_backend_client::RpcBackendClient; #[allow(async_fn_in_trait)] #[boundary_gen(in_process)] diff --git a/rust/common/src/rpc/backend_server.rs b/rust/common/src/rpc/backend_server.rs index 2feb873..36e702c 100644 --- a/rust/common/src/rpc/backend_server.rs +++ b/rust/common/src/rpc/backend_server.rs @@ -2,21 +2,21 @@ use std::net::SocketAddr; use std::time::Duration; use tokio::net::TcpStream; -use tonic::transport::Server; use tonic::Request; use tonic::Response; use tonic::Status; +use tonic::transport::Server; -use crate::rpc::backend_api::handle_grpc_request_backend_for_cli_api; -use crate::rpc::backend_api::handle_grpc_request_backend_for_settings_api; use crate::rpc::backend_api::BackendForCliApi; use crate::rpc::backend_api::BackendForSettingsApi; use crate::rpc::backend_api::BackendForToolsApi; -use crate::rpc::grpc::rpc_backend_server::RpcBackend; -use crate::rpc::grpc::rpc_backend_server::RpcBackendServer; +use crate::rpc::backend_api::handle_grpc_request_backend_for_cli_api; +use crate::rpc::backend_api::handle_grpc_request_backend_for_settings_api; use crate::rpc::grpc::RpcBincode; use crate::rpc::grpc::RpcSaveLocalPluginRequest; use crate::rpc::grpc::RpcSaveLocalPluginResponse; +use crate::rpc::grpc::rpc_backend_server::RpcBackend; +use crate::rpc::grpc::rpc_backend_server::RpcBackendServer; pub async fn wait_for_backend_server() { loop { diff --git a/rust/common/src/scenario_model.rs b/rust/common/src/scenario_model.rs index c3e8086..0bd881e 100644 --- a/rust/common/src/scenario_model.rs +++ b/rust/common/src/scenario_model.rs @@ -38,8 +38,8 @@ mod base64 { use std::collections::HashMap; use std::str::FromStr; - use base64::engine::general_purpose::STANDARD; use base64::Engine; + use base64::engine::general_purpose::STANDARD; use serde::Deserialize; use serde::Deserializer; use serde::Serialize; diff --git a/rust/common_ui/src/lib.rs b/rust/common_ui/src/lib.rs index 1cd24ca..4f8b087 100644 --- a/rust/common_ui/src/lib.rs +++ b/rust/common_ui/src/lib.rs @@ -1,14 +1,14 @@ use gauntlet_common::model::PhysicalKey; use gauntlet_common::model::PhysicalShortcut; +use iced::Element; +use iced::Padding; +use iced::Pixels; use iced::border::Radius; use iced::keyboard::Modifiers; use iced::widget::text; use iced::widget::value; -use iced::Element; -use iced::Padding; -use iced::Pixels; -use iced_aw::iced_fonts::Bootstrap; use iced_aw::iced_fonts::BOOTSTRAP_FONT; +use iced_aw::iced_fonts::Bootstrap; pub fn padding( top: impl Into, diff --git a/rust/management_client/src/components/shortcut_selector.rs b/rust/management_client/src/components/shortcut_selector.rs index 28c2658..b90d607 100644 --- a/rust/management_client/src/components/shortcut_selector.rs +++ b/rust/management_client/src/components/shortcut_selector.rs @@ -1,18 +1,27 @@ use gauntlet_common::model::PhysicalShortcut; use gauntlet_common_ui::physical_key_model; use gauntlet_common_ui::shortcut_to_text; +use iced::Alignment; +use iced::Event; +use iced::Length; +use iced::Padding; +use iced::Point; +use iced::Rectangle; +use iced::Renderer; +use iced::Size; +use iced::Vector; +use iced::advanced::Clipboard; +use iced::advanced::Layout; +use iced::advanced::Shell; +use iced::advanced::Widget; use iced::advanced::graphics::core::event; use iced::advanced::graphics::core::keyboard; use iced::advanced::layout; use iced::advanced::mouse; use iced::advanced::overlay; use iced::advanced::renderer; -use iced::advanced::widget::tree; use iced::advanced::widget::Tree; -use iced::advanced::Clipboard; -use iced::advanced::Layout; -use iced::advanced::Shell; -use iced::advanced::Widget; +use iced::advanced::widget::tree; use iced::keyboard::key::Physical; use iced::mouse::Button; use iced::widget::column; @@ -24,22 +33,13 @@ use iced::widget::text; use iced::widget::tooltip; use iced::widget::tooltip::Position; use iced::widget::value; -use iced::Alignment; -use iced::Event; -use iced::Length; -use iced::Padding; -use iced::Point; -use iced::Rectangle; -use iced::Renderer; -use iced::Size; -use iced::Vector; -use iced_fonts::Bootstrap; use iced_fonts::BOOTSTRAP_FONT; +use iced_fonts::Bootstrap; -use crate::theme::container::ContainerStyle; -use crate::theme::text::TextStyle; use crate::theme::Element; use crate::theme::GauntletSettingsTheme; +use crate::theme::container::ContainerStyle; +use crate::theme::text::TextStyle; pub struct ShortcutData { pub shortcut: Option, diff --git a/rust/management_client/src/theme/button.rs b/rust/management_client/src/theme/button.rs index 310b2e7..5e05950 100644 --- a/rust/management_client/src/theme/button.rs +++ b/rust/management_client/src/theme/button.rs @@ -1,13 +1,13 @@ +use iced::Border; use iced::widget::button; use iced::widget::button::Status; use iced::widget::button::Style; -use iced::Border; -use crate::theme::GauntletSettingsTheme; use crate::theme::BACKGROUND_DARKER; use crate::theme::BACKGROUND_LIGHTER; use crate::theme::BUTTON_BORDER_RADIUS; use crate::theme::DANGER; +use crate::theme::GauntletSettingsTheme; use crate::theme::PRIMARY; use crate::theme::PRIMARY_HOVERED; use crate::theme::SUCCESS; @@ -52,7 +52,7 @@ fn active(class: &ButtonStyle) -> Style { background: None, text_color: TEXT_LIGHTEST.to_iced(), ..Default::default() - } + }; } ButtonStyle::ViewSwitcher => { return Style { @@ -63,7 +63,7 @@ fn active(class: &ButtonStyle) -> Style { ..Default::default() }, ..Default::default() - } + }; } ButtonStyle::ViewSwitcherSelected => { return Style { @@ -74,7 +74,7 @@ fn active(class: &ButtonStyle) -> Style { ..Default::default() }, ..Default::default() - } + }; } ButtonStyle::DownloadInfo => { return Style { @@ -85,7 +85,7 @@ fn active(class: &ButtonStyle) -> Style { ..Default::default() }, ..Default::default() - } + }; } }; @@ -121,7 +121,7 @@ fn hovered(class: &ButtonStyle) -> Style { ..Default::default() }, ..Default::default() - } + }; } ButtonStyle::ViewSwitcherSelected => { return Style { @@ -132,7 +132,7 @@ fn hovered(class: &ButtonStyle) -> Style { ..Default::default() }, ..Default::default() - } + }; } ButtonStyle::DownloadInfo => { return Style { @@ -143,7 +143,7 @@ fn hovered(class: &ButtonStyle) -> Style { ..Default::default() }, ..Default::default() - } + }; } }; diff --git a/rust/management_client/src/theme/checkbox.rs b/rust/management_client/src/theme/checkbox.rs index fe9128a..4e744d6 100644 --- a/rust/management_client/src/theme/checkbox.rs +++ b/rust/management_client/src/theme/checkbox.rs @@ -1,12 +1,12 @@ +use iced::Border; use iced::widget::checkbox; use iced::widget::checkbox::Status; use iced::widget::checkbox::Style; -use iced::Border; -use crate::theme::GauntletSettingsTheme; use crate::theme::BACKGROUND_DARKER; use crate::theme::BACKGROUND_DARKEST; use crate::theme::BACKGROUND_LIGHTER; +use crate::theme::GauntletSettingsTheme; use crate::theme::PRIMARY; use crate::theme::PRIMARY_HOVERED; diff --git a/rust/management_client/src/theme/container.rs b/rust/management_client/src/theme/container.rs index dc562a2..00f7095 100644 --- a/rust/management_client/src/theme/container.rs +++ b/rust/management_client/src/theme/container.rs @@ -1,12 +1,12 @@ -use iced::widget::container; -use iced::widget::container::Style; use iced::Border; use iced::Color; +use iced::widget::container; +use iced::widget::container::Style; -use crate::theme::GauntletSettingsTheme; use crate::theme::BACKGROUND_DARKER; use crate::theme::BACKGROUND_LIGHTER; use crate::theme::DANGER; +use crate::theme::GauntletSettingsTheme; use crate::theme::TRANSPARENT; pub enum ContainerStyle { diff --git a/rust/management_client/src/theme/number_input.rs b/rust/management_client/src/theme/number_input.rs index dce1110..a2a663e 100644 --- a/rust/management_client/src/theme/number_input.rs +++ b/rust/management_client/src/theme/number_input.rs @@ -1,5 +1,5 @@ -use iced_aw::number_input::number_input; use iced_aw::number_input::Style; +use iced_aw::number_input::number_input; use iced_aw::style::Status; use crate::theme::GauntletSettingsTheme; diff --git a/rust/management_client/src/theme/pick_list.rs b/rust/management_client/src/theme/pick_list.rs index 49a6e56..7028f0b 100644 --- a/rust/management_client/src/theme/pick_list.rs +++ b/rust/management_client/src/theme/pick_list.rs @@ -1,11 +1,11 @@ +use iced::Border; use iced::overlay; use iced::widget::pick_list; -use iced::Border; -use crate::theme::GauntletSettingsTheme; use crate::theme::BACKGROUND_DARKER; use crate::theme::BACKGROUND_DARKEST; use crate::theme::BUTTON_BORDER_RADIUS; +use crate::theme::GauntletSettingsTheme; use crate::theme::PRIMARY; use crate::theme::PRIMARY_HOVERED; use crate::theme::TEXT_DARKEST; diff --git a/rust/management_client/src/theme/rule.rs b/rust/management_client/src/theme/rule.rs index caa3215..45aaf15 100644 --- a/rust/management_client/src/theme/rule.rs +++ b/rust/management_client/src/theme/rule.rs @@ -1,8 +1,8 @@ use iced::widget::rule; use iced::widget::rule::Style; -use crate::theme::GauntletSettingsTheme; use crate::theme::BACKGROUND_DARKER; +use crate::theme::GauntletSettingsTheme; impl rule::Catalog for GauntletSettingsTheme { type Class<'a> = (); diff --git a/rust/management_client/src/theme/scrollable.rs b/rust/management_client/src/theme/scrollable.rs index fd46c59..fc79c84 100644 --- a/rust/management_client/src/theme/scrollable.rs +++ b/rust/management_client/src/theme/scrollable.rs @@ -1,10 +1,10 @@ +use iced::Border; +use iced::Color; use iced::border; use iced::widget::container; use iced::widget::scrollable; use iced::widget::scrollable::Status; use iced::widget::scrollable::Style; -use iced::Border; -use iced::Color; use crate::theme::GauntletSettingsTheme; use crate::theme::PRIMARY; diff --git a/rust/management_client/src/theme/shortcut_selector.rs b/rust/management_client/src/theme/shortcut_selector.rs index 68a227b..09dc383 100644 --- a/rust/management_client/src/theme/shortcut_selector.rs +++ b/rust/management_client/src/theme/shortcut_selector.rs @@ -1,12 +1,12 @@ -use iced::widget::container::Style; use iced::Border; +use iced::widget::container::Style; use crate::components::shortcut_selector; use crate::components::shortcut_selector::Status; -use crate::theme::GauntletSettingsTheme; use crate::theme::BACKGROUND_DARKER; use crate::theme::BACKGROUND_LIGHTER; use crate::theme::BUTTON_BORDER_RADIUS; +use crate::theme::GauntletSettingsTheme; use crate::theme::PRIMARY; use crate::theme::TRANSPARENT; diff --git a/rust/management_client/src/theme/table.rs b/rust/management_client/src/theme/table.rs index 69b625a..4c4b71c 100644 --- a/rust/management_client/src/theme/table.rs +++ b/rust/management_client/src/theme/table.rs @@ -1,8 +1,8 @@ -use iced::widget::container; use iced::Border; +use iced::widget::container; -use crate::theme::GauntletSettingsTheme; use crate::theme::BACKGROUND_DARKER; +use crate::theme::GauntletSettingsTheme; use crate::theme::TEXT_LIGHTEST; impl iced_table::Catalog for GauntletSettingsTheme { diff --git a/rust/management_client/src/theme/text.rs b/rust/management_client/src/theme/text.rs index e7e6cea..be46ec7 100644 --- a/rust/management_client/src/theme/text.rs +++ b/rust/management_client/src/theme/text.rs @@ -1,8 +1,8 @@ use iced::widget::text; use iced::widget::text::Style; -use crate::theme::GauntletSettingsTheme; use crate::theme::DANGER_BRIGHT; +use crate::theme::GauntletSettingsTheme; use crate::theme::SUCCESS; use crate::theme::TEXT_DARKER; diff --git a/rust/management_client/src/theme/text_input.rs b/rust/management_client/src/theme/text_input.rs index c45e447..1015b36 100644 --- a/rust/management_client/src/theme/text_input.rs +++ b/rust/management_client/src/theme/text_input.rs @@ -1,14 +1,14 @@ +use iced::Background; +use iced::Border; use iced::widget::text_input; use iced::widget::text_input::Status; use iced::widget::text_input::Style; -use iced::Background; -use iced::Border; -use crate::theme::GauntletSettingsTheme; use crate::theme::BACKGROUND_DARKER; use crate::theme::BACKGROUND_LIGHTER; use crate::theme::BACKGROUND_LIGHTEST; use crate::theme::BUTTON_BORDER_RADIUS; +use crate::theme::GauntletSettingsTheme; use crate::theme::TEXT_DARKER; use crate::theme::TEXT_LIGHTEST; use crate::theme::TRANSPARENT; diff --git a/rust/management_client/src/ui.rs b/rust/management_client/src/ui.rs index ce027e7..9921827 100644 --- a/rust/management_client/src/ui.rs +++ b/rust/management_client/src/ui.rs @@ -13,6 +13,13 @@ use gauntlet_common::rpc::backend_api::GrpcBackendApi; use gauntlet_common_ui::padding; use gauntlet_utils::channel::RequestError; use gauntlet_utils::channel::RequestResult; +use iced::Alignment; +use iced::Length; +use iced::Padding; +use iced::Renderer; +use iced::Size; +use iced::Subscription; +use iced::Task; use iced::advanced::text::Shaping; use iced::alignment; use iced::font; @@ -31,24 +38,17 @@ use iced::widget::stack; use iced::widget::text; use iced::widget::value; use iced::window; -use iced::Alignment; -use iced::Length; -use iced::Padding; -use iced::Renderer; -use iced::Size; -use iced::Subscription; -use iced::Task; use iced_aw::Spinner; -use iced_fonts::Bootstrap; use iced_fonts::BOOTSTRAP_FONT; use iced_fonts::BOOTSTRAP_FONT_BYTES; +use iced_fonts::Bootstrap; use itertools::Itertools; +use crate::theme::Element; +use crate::theme::GauntletSettingsTheme; use crate::theme::button::ButtonStyle; use crate::theme::container::ContainerStyle; use crate::theme::text::TextStyle; -use crate::theme::Element; -use crate::theme::GauntletSettingsTheme; use crate::views::general::ManagementAppGeneralMsgIn; use crate::views::general::ManagementAppGeneralMsgOut; use crate::views::general::ManagementAppGeneralState; diff --git a/rust/management_client/src/views/general.rs b/rust/management_client/src/views/general.rs index 9a0251a..3ec34cd 100644 --- a/rust/management_client/src/views/general.rs +++ b/rust/management_client/src/views/general.rs @@ -4,25 +4,25 @@ use gauntlet_common::model::WindowPositionMode; use gauntlet_common::rpc::backend_api::BackendForSettingsApi; use gauntlet_common::rpc::backend_api::BackendForSettingsApiProxy; use gauntlet_utils::channel::RequestResult; +use iced::Alignment; +use iced::Length; +use iced::Padding; +use iced::Task; use iced::alignment; use iced::alignment::Horizontal; +use iced::widget::Space; use iced::widget::column; use iced::widget::container; use iced::widget::pick_list; use iced::widget::row; use iced::widget::text; use iced::widget::text::Shaping; -use iced::widget::Space; -use iced::Alignment; -use iced::Length; -use iced::Padding; -use iced::Task; +use crate::components::shortcut_selector::ShortcutData; use crate::components::shortcut_selector::render_shortcut_error; use crate::components::shortcut_selector::shortcut_selector; -use crate::components::shortcut_selector::ShortcutData; -use crate::theme::container::ContainerStyle; use crate::theme::Element; +use crate::theme::container::ContainerStyle; use crate::ui::ManagementAppMsg; pub struct ManagementAppGeneralState { diff --git a/rust/management_client/src/views/plugins.rs b/rust/management_client/src/views/plugins.rs index 231d3fb..d15eaa5 100644 --- a/rust/management_client/src/views/plugins.rs +++ b/rust/management_client/src/views/plugins.rs @@ -2,6 +2,8 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; +use gauntlet_common::SETTINGS_ENV; +use gauntlet_common::SettingsEnvData; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; @@ -11,9 +13,11 @@ use gauntlet_common::model::SettingsPlugin; use gauntlet_common::rpc::backend_api::BackendForSettingsApi; use gauntlet_common::rpc::backend_api::BackendForSettingsApiProxy; use gauntlet_common::settings_env_data_from_string; -use gauntlet_common::SettingsEnvData; -use gauntlet_common::SETTINGS_ENV; use gauntlet_utils::channel::RequestResult; +use iced::Alignment; +use iced::Length; +use iced::Padding; +use iced::Task; use iced::padding; use iced::widget::button; use iced::widget::column; @@ -25,20 +29,16 @@ use iced::widget::text::Shaping; use iced::widget::text_input; use iced::widget::value; use iced::widget::vertical_rule; -use iced::Alignment; -use iced::Length; -use iced::Padding; -use iced::Task; -use iced_fonts::Bootstrap; use iced_fonts::BOOTSTRAP_FONT; +use iced_fonts::Bootstrap; +use crate::theme::Element; use crate::theme::button::ButtonStyle; use crate::theme::text::TextStyle; -use crate::theme::Element; use crate::ui::ManagementAppMsg; -use crate::views::plugins::preferences::preferences_ui; use crate::views::plugins::preferences::PluginPreferencesMsg; use crate::views::plugins::preferences::SelectItem; +use crate::views::plugins::preferences::preferences_ui; use crate::views::plugins::table::PluginTableMsgIn; use crate::views::plugins::table::PluginTableMsgOut; use crate::views::plugins::table::PluginTableState; diff --git a/rust/management_client/src/views/plugins/preferences.rs b/rust/management_client/src/views/plugins/preferences.rs index 3e8e1b9..c5a700a 100644 --- a/rust/management_client/src/views/plugins/preferences.rs +++ b/rust/management_client/src/views/plugins/preferences.rs @@ -4,6 +4,8 @@ use std::fmt::Display; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PluginId; use gauntlet_common::model::PluginPreference; +use iced::Length; +use iced::Padding; use iced::padding; use iced::widget; use iced::widget::button; @@ -15,16 +17,14 @@ use iced::widget::row; use iced::widget::text; use iced::widget::text::Shaping; use iced::widget::text_input; -use iced::Length; -use iced::Padding; use iced_aw::number_input; -use iced_fonts::Bootstrap; use iced_fonts::BOOTSTRAP_FONT; +use iced_fonts::Bootstrap; +use crate::theme::Element; use crate::theme::button::ButtonStyle; use crate::theme::container::ContainerStyle; use crate::theme::text::TextStyle; -use crate::theme::Element; use crate::views::plugins::PluginPreferenceUserDataState; #[derive(Debug, Clone)] diff --git a/rust/management_client/src/views/plugins/table.rs b/rust/management_client/src/views/plugins/table.rs index ef0da2e..5e0f5a4 100644 --- a/rust/management_client/src/views/plugins/table.rs +++ b/rust/management_client/src/views/plugins/table.rs @@ -7,8 +7,13 @@ use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; use gauntlet_common::model::SettingsEntrypointType; use gauntlet_common::model::SettingsPlugin; +use iced::Alignment; +use iced::Length; +use iced::Renderer; +use iced::Task; use iced::advanced::text::Shaping; use iced::padding; +use iced::widget::Space; use iced::widget::button; use iced::widget::checkbox; use iced::widget::container; @@ -19,22 +24,17 @@ use iced::widget::scrollable::Id; use iced::widget::text; use iced::widget::text_input; use iced::widget::value; -use iced::widget::Space; -use iced::Alignment; -use iced::Length; -use iced::Renderer; -use iced::Task; -use iced_fonts::Bootstrap; use iced_fonts::BOOTSTRAP_FONT; +use iced_fonts::Bootstrap; use iced_table::table; -use crate::components::shortcut_selector::shortcut_selector; use crate::components::shortcut_selector::ShortcutData; +use crate::components::shortcut_selector::shortcut_selector; +use crate::theme::Element; +use crate::theme::GauntletSettingsTheme; use crate::theme::button::ButtonStyle; use crate::theme::container::ContainerStyle; use crate::theme::text_input::TextInputStyle; -use crate::theme::Element; -use crate::theme::GauntletSettingsTheme; use crate::views::plugins::PluginDataContainer; use crate::views::plugins::SelectedItem; use crate::views::plugins::SettingsPluginData; diff --git a/rust/plugin_runtime/src/assets.rs b/rust/plugin_runtime/src/assets.rs index d611e17..f5f9f74 100644 --- a/rust/plugin_runtime/src/assets.rs +++ b/rust/plugin_runtime/src/assets.rs @@ -1,9 +1,9 @@ use std::cell::RefCell; use std::rc::Rc; +use deno_core::OpState; use deno_core::futures::executor::block_on; use deno_core::op2; -use deno_core::OpState; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; diff --git a/rust/plugin_runtime/src/clipboard.rs b/rust/plugin_runtime/src/clipboard.rs index 5cb917c..c11bb30 100644 --- a/rust/plugin_runtime/src/clipboard.rs +++ b/rust/plugin_runtime/src/clipboard.rs @@ -1,8 +1,8 @@ use std::cell::RefCell; use std::rc::Rc; -use deno_core::op2; use deno_core::OpState; +use deno_core::op2; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use gauntlet_common_plugin_runtime::model::JsClipboardData; diff --git a/rust/plugin_runtime/src/component_model.rs b/rust/plugin_runtime/src/component_model.rs index e4134f7..a3c8128 100644 --- a/rust/plugin_runtime/src/component_model.rs +++ b/rust/plugin_runtime/src/component_model.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; -use gauntlet_component_model::create_component_model; use gauntlet_component_model::Component; +use gauntlet_component_model::create_component_model; pub struct ComponentModel { components: HashMap, diff --git a/rust/plugin_runtime/src/entrypoint_generators.rs b/rust/plugin_runtime/src/entrypoint_generators.rs index 0117511..ae10b0c 100644 --- a/rust/plugin_runtime/src/entrypoint_generators.rs +++ b/rust/plugin_runtime/src/entrypoint_generators.rs @@ -1,8 +1,8 @@ use std::cell::RefCell; use std::rc::Rc; -use deno_core::op2; use deno_core::OpState; +use deno_core::op2; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; diff --git a/rust/plugin_runtime/src/environment.rs b/rust/plugin_runtime/src/environment.rs index ca499e6..c1ed4d4 100644 --- a/rust/plugin_runtime/src/environment.rs +++ b/rust/plugin_runtime/src/environment.rs @@ -1,5 +1,5 @@ -use deno_core::op2; use deno_core::OpState; +use deno_core::op2; use crate::plugin_data::PluginData; diff --git a/rust/plugin_runtime/src/logs.rs b/rust/plugin_runtime/src/logs.rs index 4bd75d8..3dd34c3 100644 --- a/rust/plugin_runtime/src/logs.rs +++ b/rust/plugin_runtime/src/logs.rs @@ -1,8 +1,8 @@ use std::cell::RefCell; use std::rc::Rc; -use deno_core::op2; use deno_core::OpState; +use deno_core::op2; use crate::plugin_data::PluginData; diff --git a/rust/plugin_runtime/src/plugins/applications/macos.rs b/rust/plugin_runtime/src/plugins/applications/macos.rs index 59af25b..330dddc 100644 --- a/rust/plugin_runtime/src/plugins/applications/macos.rs +++ b/rust/plugin_runtime/src/plugins/applications/macos.rs @@ -5,8 +5,8 @@ use std::path::Component; use std::path::Path; use std::path::PathBuf; -use anyhow::anyhow; use anyhow::Context; +use anyhow::anyhow; use cacao::filesystem::FileManager; use cacao::filesystem::SearchPathDirectory; use cacao::filesystem::SearchPathDomainMask; diff --git a/rust/plugin_runtime/src/plugins/applications/windows.rs b/rust/plugin_runtime/src/plugins/applications/windows.rs index 9d6a25c..a30ecbe 100644 --- a/rust/plugin_runtime/src/plugins/applications/windows.rs +++ b/rust/plugin_runtime/src/plugins/applications/windows.rs @@ -4,15 +4,12 @@ use std::mem::MaybeUninit; use std::path::PathBuf; use std::ptr; -use anyhow::anyhow; use anyhow::Context; -use deno_core::op2; +use anyhow::anyhow; use deno_core::ToJsBuffer; +use deno_core::op2; use image::RgbaImage; use tokio::task::spawn_blocking; -use windows::core::GUID; -use windows::core::HSTRING; -use windows::core::PWSTR; use windows::Win32::Foundation::HANDLE; use windows::Win32::Foundation::HWND; use windows::Win32::Graphics::Gdi; @@ -22,10 +19,13 @@ use windows::Win32::UI::Controls; use windows::Win32::UI::Controls::HIMAGELIST; use windows::Win32::UI::Shell; use windows::Win32::UI::WindowsAndMessaging; +use windows::core::GUID; +use windows::core::HSTRING; +use windows::core::PWSTR; -use crate::plugins::applications::resize_icon; use crate::plugins::applications::DesktopApplication; use crate::plugins::applications::DesktopPathAction; +use crate::plugins::applications::resize_icon; deno_core::extension!( gauntlet_internal_windows, diff --git a/rust/plugin_runtime/src/plugins/numbat.rs b/rust/plugin_runtime/src/plugins/numbat.rs index 02f267f..e28b22a 100644 --- a/rust/plugin_runtime/src/plugins/numbat.rs +++ b/rust/plugin_runtime/src/plugins/numbat.rs @@ -2,15 +2,15 @@ use std::cell::RefCell; use std::rc::Rc; use anyhow::anyhow; -use deno_core::op2; use deno_core::OpState; +use deno_core::op2; +use numbat::Context; +use numbat::InterpreterResult; use numbat::markup::Formatter; use numbat::markup::PlainTextFormatter; use numbat::module_importer::BuiltinModuleImporter; use numbat::pretty_print::PrettyPrint; use numbat::resolver::CodeSource; -use numbat::Context; -use numbat::InterpreterResult; use serde::Serialize; #[derive(Clone)] diff --git a/rust/plugin_runtime/src/preferences.rs b/rust/plugin_runtime/src/preferences.rs index 8aa2672..294a512 100644 --- a/rust/plugin_runtime/src/preferences.rs +++ b/rust/plugin_runtime/src/preferences.rs @@ -2,9 +2,9 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; +use deno_core::OpState; use deno_core::futures::executor::block_on; use deno_core::op2; -use deno_core::OpState; use gauntlet_common::model::EntrypointId; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; diff --git a/rust/plugin_runtime/src/search.rs b/rust/plugin_runtime/src/search.rs index 6f8d784..26c1e0c 100644 --- a/rust/plugin_runtime/src/search.rs +++ b/rust/plugin_runtime/src/search.rs @@ -1,8 +1,8 @@ use std::cell::RefCell; use std::rc::Rc; -use deno_core::op2; use deno_core::OpState; +use deno_core::op2; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use gauntlet_common_plugin_runtime::model::JsGeneratedSearchItem; diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 7863cc0..fdd1bce 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -12,19 +12,19 @@ use gauntlet_client::start_client; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PluginId; -use gauntlet_common::rpc::backend_api::handle_proxy_message; use gauntlet_common::rpc::backend_api::BackendForCliApi; use gauntlet_common::rpc::backend_api::BackendForCliApiProxy; use gauntlet_common::rpc::backend_api::BackendForFrontendApiRequestData; use gauntlet_common::rpc::backend_api::BackendForFrontendApiResponseData; use gauntlet_common::rpc::backend_api::GrpcBackendApi; +use gauntlet_common::rpc::backend_api::handle_proxy_message; use gauntlet_common::rpc::backend_server::start_backend_server; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; -use gauntlet_utils::channel::channel; use gauntlet_utils::channel::RequestError; use gauntlet_utils::channel::RequestReceiver; use gauntlet_utils::channel::RequestSender; +use gauntlet_utils::channel::channel; use vergen_pretty::vergen_pretty_env; use crate::plugins::ApplicationManager; diff --git a/rust/server/src/plugins/clipboard.rs b/rust/server/src/plugins/clipboard.rs index 503e506..2d5adc2 100644 --- a/rust/server/src/plugins/clipboard.rs +++ b/rust/server/src/plugins/clipboard.rs @@ -2,9 +2,9 @@ use std::io::Cursor; use std::sync::Arc; use std::sync::RwLock; -use anyhow::anyhow; use anyhow::Context; use anyhow::Error; +use anyhow::anyhow; use arboard::ImageData; use gauntlet_common_plugin_runtime::model::JsClipboardData; use image::RgbaImage; diff --git a/rust/server/src/plugins/data_db_repository.rs b/rust/server/src/plugins/data_db_repository.rs index 35f17ef..56d6916 100644 --- a/rust/server/src/plugins/data_db_repository.rs +++ b/rust/server/src/plugins/data_db_repository.rs @@ -1,24 +1,24 @@ use std::collections::HashMap; use std::collections::HashSet; -use anyhow::anyhow; use anyhow::Context; -use futures::future::join_all; +use anyhow::anyhow; use futures::StreamExt; use futures::TryStreamExt; +use futures::future::join_all; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::PhysicalKey; use gauntlet_common::model::PhysicalShortcut; use serde::Deserialize; use serde::Serialize; -use sqlx::migrate::Migrator; -use sqlx::sqlite::SqliteConnectOptions; -use sqlx::types::Json; use sqlx::Executor; use sqlx::Pool; use sqlx::Row; use sqlx::Sqlite; use sqlx::SqlitePool; +use sqlx::migrate::Migrator; +use sqlx::sqlite::SqliteConnectOptions; +use sqlx::types::Json; use uuid::Uuid; use crate::model::ActionShortcutKey; diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 8a4a8d8..771961e 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -2,8 +2,8 @@ use std::collections::HashMap; use std::fs::File; use std::sync::Arc; -use anyhow::anyhow; use anyhow::Context; +use anyhow::anyhow; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::KeyboardEventOrigin; @@ -17,8 +17,9 @@ use gauntlet_common::model::UiRenderLocation; use gauntlet_common::model::UiWidgetId; use gauntlet_common::rpc::frontend_api::FrontendApi; use gauntlet_common::rpc::frontend_api::FrontendApiProxy; -use gauntlet_common_plugin_runtime::api::handle_proxy_message; +use gauntlet_common_plugin_runtime::JsMessageSide; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; +use gauntlet_common_plugin_runtime::api::handle_proxy_message; use gauntlet_common_plugin_runtime::model::JsClipboardData; use gauntlet_common_plugin_runtime::model::JsEvent; use gauntlet_common_plugin_runtime::model::JsGeneratedSearchItem; @@ -38,36 +39,35 @@ use gauntlet_common_plugin_runtime::model::JsUiPropertyValue; use gauntlet_common_plugin_runtime::model::JsUiRenderLocation; use gauntlet_common_plugin_runtime::recv_message; use gauntlet_common_plugin_runtime::send_message; -use gauntlet_common_plugin_runtime::JsMessageSide; use gauntlet_utils::channel::RequestResult; +use interprocess::local_socket::ListenerOptions; +use interprocess::local_socket::ToFsName; use interprocess::local_socket::tokio::RecvHalf; use interprocess::local_socket::tokio::SendHalf; use interprocess::local_socket::traits::tokio::Listener; use interprocess::local_socket::traits::tokio::Stream; -use interprocess::local_socket::ListenerOptions; -use interprocess::local_socket::ToFsName; use serde::Deserialize; use serde::Serialize; use tokio::sync::Mutex; +use crate::PLUGIN_CONNECT_ENV; +use crate::PLUGIN_UUID_ENV; use crate::model::IntermediateUiEvent; use crate::plugins::binary_data_gatherer::BinaryDataGatherer; use crate::plugins::clipboard::Clipboard; -use crate::plugins::data_db_repository::db_entrypoint_from_str; use crate::plugins::data_db_repository::DataDbRepository; use crate::plugins::data_db_repository::DbPluginEntrypointType; use crate::plugins::data_db_repository::DbPluginPreference; use crate::plugins::data_db_repository::DbPluginPreferenceUserData; use crate::plugins::data_db_repository::DbReadPlugin; use crate::plugins::data_db_repository::DbReadPluginEntrypoint; +use crate::plugins::data_db_repository::db_entrypoint_from_str; use crate::plugins::icon_cache::IconCache; use crate::plugins::run_status::RunStatusGuard; use crate::search::SearchIndex; use crate::search::SearchIndexItem; use crate::search::SearchIndexItemAction; use crate::search::SearchIndexItemActionActionType; -use crate::PLUGIN_CONNECT_ENV; -use crate::PLUGIN_UUID_ENV; pub struct PluginRuntimeData { pub id: PluginId, diff --git a/rust/server/src/plugins/loader.rs b/rust/server/src/plugins/loader.rs index d5a9360..00be5f3 100644 --- a/rust/server/src/plugins/loader.rs +++ b/rust/server/src/plugins/loader.rs @@ -5,8 +5,8 @@ use std::io::ErrorKind; use std::path::Path; use std::thread; -use anyhow::anyhow; use anyhow::Context; +use anyhow::anyhow; use gauntlet_common::model::DownloadStatus; use gauntlet_common::model::PluginId; use gauntlet_common_plugin_runtime::PERMISSIONS_VARIABLE_PATTERN; @@ -18,8 +18,6 @@ use typed_path::Utf8WindowsComponent; use typed_path::Utf8WindowsPrefix; use walkdir::WalkDir; -use crate::plugins::data_db_repository::db_entrypoint_to_str; -use crate::plugins::data_db_repository::db_plugin_type_to_str; use crate::plugins::data_db_repository::DataDbRepository; use crate::plugins::data_db_repository::DbCode; use crate::plugins::data_db_repository::DbPluginAction; @@ -36,6 +34,8 @@ use crate::plugins::data_db_repository::DbPreferenceEnumValue; use crate::plugins::data_db_repository::DbWritePlugin; use crate::plugins::data_db_repository::DbWritePluginAssetData; use crate::plugins::data_db_repository::DbWritePluginEntrypoint; +use crate::plugins::data_db_repository::db_entrypoint_to_str; +use crate::plugins::data_db_repository::db_plugin_type_to_str; use crate::plugins::download_status::DownloadStatusHolder; use crate::plugins::plugin_manifest::*; @@ -719,7 +719,11 @@ impl PluginLoader { Some(b'/') | None => false, Some(byte) => { // this is done to prohibit "{linux:user-home}test" which for variable "/home/user" would result into "/home/usertest" - Err(anyhow!("Variable should always be followed with a slash or end of string, instead followed with {}, path: {}", byte as char, path))? + Err(anyhow!( + "Variable should always be followed with a slash or end of string, instead followed with {}, path: {}", + byte as char, + path + ))? } }; @@ -759,7 +763,10 @@ impl PluginLoader { match path { Utf8TypedPath::Unix(path) => { if !supports_macos && !supports_linux { - Err(anyhow!("When using unix-style path in permissions, plugin is required to include \"linux\" or \"macos\" in \"supported_system\" manifest property: {}", path))? + Err(anyhow!( + "When using unix-style path in permissions, plugin is required to include \"linux\" or \"macos\" in \"supported_system\" manifest property: {}", + path + ))? } if !path.is_valid() { @@ -786,7 +793,10 @@ impl PluginLoader { } Utf8TypedPath::Windows(path) => { if !supports_windows { - Err(anyhow!("When using windows-style path in permissions, plugin is required to include \"windows\" in \"supported_system\" manifest property: {}", path))? + Err(anyhow!( + "When using windows-style path in permissions, plugin is required to include \"windows\" in \"supported_system\" manifest property: {}", + path + ))? } if !path.is_valid() { diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 22e6667..df97083 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; use anyhow::anyhow; +use gauntlet_common::SETTINGS_ENV; +use gauntlet_common::SettingsEnvData; use gauntlet_common::detached_process::CommandExt; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::DownloadStatus; @@ -30,35 +32,33 @@ use gauntlet_common::rpc::frontend_api::FrontendApiProxy; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; use gauntlet_common::settings_env_data_to_string; -use gauntlet_common::SettingsEnvData; -use gauntlet_common::SETTINGS_ENV; use gauntlet_common_plugin_runtime::model::JsPluginCode; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsExec; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsFileSystem; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsMainSearchBar; use gauntlet_utils::channel::RequestResult; use gauntlet_utils::channel::RequestSender; -use include_dir::include_dir; use include_dir::Dir; +use include_dir::include_dir; use itertools::Itertools; use crate::plugins::clipboard::Clipboard; use crate::plugins::config_reader::ConfigReader; -use crate::plugins::data_db_repository::db_entrypoint_from_str; use crate::plugins::data_db_repository::DataDbRepository; use crate::plugins::data_db_repository::DbPluginClipboardPermissions; use crate::plugins::data_db_repository::DbPluginEntrypointType; use crate::plugins::data_db_repository::DbPluginMainSearchBarPermissions; use crate::plugins::data_db_repository::DbPluginPreference; use crate::plugins::data_db_repository::DbPluginPreferenceUserData; +use crate::plugins::data_db_repository::db_entrypoint_from_str; use crate::plugins::icon_cache::IconCache; -use crate::plugins::js::start_plugin_runtime; use crate::plugins::js::AllPluginCommandData; use crate::plugins::js::OnePluginCommandData; use crate::plugins::js::PluginCommand; use crate::plugins::js::PluginPermissions; use crate::plugins::js::PluginPermissionsClipboard; use crate::plugins::js::PluginRuntimeData; +use crate::plugins::js::start_plugin_runtime; use crate::plugins::loader::PluginLoader; use crate::plugins::run_status::RunStatusHolder; use crate::plugins::settings::Settings; diff --git a/rust/server/src/plugins/settings.rs b/rust/server/src/plugins/settings.rs index 18954af..fd483bd 100644 --- a/rust/server/src/plugins/settings.rs +++ b/rust/server/src/plugins/settings.rs @@ -21,8 +21,8 @@ use crate::plugins::data_db_repository::DbSettingsGlobalShortcutData; use crate::plugins::data_db_repository::DbSettingsShortcut; use crate::plugins::data_db_repository::DbTheme; use crate::plugins::data_db_repository::DbWindowPositionMode; -use crate::plugins::theme::read_theme_file; use crate::plugins::theme::BundledThemes; +use crate::plugins::theme::read_theme_file; #[derive(Clone)] pub struct Settings { diff --git a/rust/server/src/plugins/theme.rs b/rust/server/src/plugins/theme.rs index e021b5c..958c764 100644 --- a/rust/server/src/plugins/theme.rs +++ b/rust/server/src/plugins/theme.rs @@ -1,8 +1,8 @@ use std::io::ErrorKind; use std::path::PathBuf; -use anyhow::anyhow; use anyhow::Context; +use anyhow::anyhow; use gauntlet_common::model::UiTheme; use gauntlet_common::model::UiThemeColor; use gauntlet_common::model::UiThemeContent; diff --git a/rust/server/src/search.rs b/rust/server/src/search.rs index a40d9f4..28774a2 100644 --- a/rust/server/src/search.rs +++ b/rust/server/src/search.rs @@ -13,6 +13,10 @@ use gauntlet_common::model::SearchResultEntrypointActionType; use gauntlet_common::model::SearchResultEntrypointType; use gauntlet_common::rpc::frontend_api::FrontendApi; use gauntlet_common::rpc::frontend_api::FrontendApiProxy; +use tantivy::Index; +use tantivy::IndexReader; +use tantivy::ReloadPolicy; +use tantivy::Searcher; use tantivy::collector::TopDocs; use tantivy::doc; use tantivy::query::AllQuery; @@ -22,10 +26,6 @@ use tantivy::query::RegexQuery; use tantivy::query::TermQuery; use tantivy::schema::*; use tantivy::tokenizer::TokenizerManager; -use tantivy::Index; -use tantivy::IndexReader; -use tantivy::ReloadPolicy; -use tantivy::Searcher; use crate::plugins::settings::Settings; From a0275cce28652d647ad13d71ba836dcd3c5923df Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 25 May 2025 14:36:07 +0200 Subject: [PATCH 07/91] Revert plugin runtime --extern rustc flag --- .cargo/config.toml | 5 - Cargo.lock | 4964 ++++++++++++++- Cargo.toml | 6 +- js/build/src/main.ts | 8 +- js/scenario_runner_cli/src/main.ts | 6 - rust/plugin_runtime/Cargo.lock | 9259 ---------------------------- rust/plugin_runtime/Cargo.toml | 39 +- rust/server/Cargo.toml | 5 +- rust/server/src/lib.rs | 1 - rust/utils_macros/Cargo.toml | 2 +- 10 files changed, 4897 insertions(+), 9398 deletions(-) delete mode 100644 .cargo/config.toml delete mode 100644 rust/plugin_runtime/Cargo.lock diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 67d4afc..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -[build] -rustflags = """ ---extern gauntlet_plugin_runtime=./rust/plugin_runtime/target/release/libgauntlet_plugin_runtime.rlib --L dependency=./rust/plugin_runtime/target/release/deps -""" diff --git a/Cargo.lock b/Cargo.lock index 6212f61..0fc1077 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "ab_glyph" version = "0.2.29" @@ -27,12 +37,82 @@ dependencies = [ "gimli", ] +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aead-gcm-stream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4947a169074c7e038fa43051d1c4e073f4488b0e4b0a30658f1e1a1b06449ce8" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aes-kw" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" +dependencies = [ + "aes", +] + [[package]] name = "ahash" version = "0.7.8" @@ -72,6 +152,21 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -240,6 +335,9 @@ name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] [[package]] name = "as-raw-xcb-connection" @@ -247,6 +345,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + [[package]] name = "ash" version = "0.38.0+1.3.281" @@ -256,6 +363,57 @@ dependencies = [ "libloading 0.8.6", ] +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom 7.1.3", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure 0.12.6", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ast_node" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9184f2b369b3e8625712493c89b785881f27eedc6cde480a81883cef78868b2" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + [[package]] name = "async-broadcast" version = "0.7.1" @@ -280,6 +438,20 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-compression" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +dependencies = [ + "brotli 7.0.0", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-executor" version = "1.13.1" @@ -459,6 +631,19 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "attohttpc" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "184f5e6cce583a9db6b6f8d772a42cfce5b78e7c3ef26118cec3ce4c8c14969b" +dependencies = [ + "http 1.2.0", + "log", + "rustls 0.22.4", + "url", + "webpki-roots", +] + [[package]] name = "auto-launch" version = "0.5.0" @@ -467,7 +652,7 @@ checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471" dependencies = [ "dirs 4.0.0", "thiserror 1.0.69", - "winreg", + "winreg 0.10.1", ] [[package]] @@ -485,7 +670,7 @@ dependencies = [ "anyhow", "arrayvec", "log", - "nom", + "nom 7.1.3", "num-rational", "v_frame", ] @@ -509,8 +694,8 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http", - "http-body", + "http 1.2.0", + "http-body 1.0.1", "http-body-util", "itoa", "matchit", @@ -535,8 +720,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.2.0", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", @@ -555,24 +740,88 @@ dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.8.2", "object", "rustc-demangle", "windows-targets 0.52.6", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base32" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" +dependencies = [ + "simd-abstraction", +] + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref 0.5.1", + "vsimd", +] + [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "basic-toml" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +dependencies = [ + "serde", +] + +[[package]] +name = "better_scoped_tls" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297b153aa5e573b5863108a6ddc9d5c968bd0b20e75cc614ee9821d2f45679c7" +dependencies = [ + "scoped-tls", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bincode" version = "2.0.0-rc.3" @@ -592,15 +841,50 @@ dependencies = [ "virtue", ] +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.101", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec 0.6.3", +] + [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec", + "bit-vec 0.8.0", ] +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bit-vec" version = "0.8.0" @@ -643,6 +927,27 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + [[package]] name = "block" version = "0.1.6" @@ -658,6 +963,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "block2" version = "0.5.1" @@ -680,6 +994,48 @@ dependencies = [ "piper", ] +[[package]] +name = "boxed_error" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" +dependencies = [ + "quote", + "syn 2.0.101", +] + +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bstr" version = "1.11.1" @@ -701,6 +1057,9 @@ name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +dependencies = [ + "allocator-api2", +] [[package]] name = "by_address" @@ -746,6 +1105,30 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +[[package]] +name = "cacao" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5952f0958672e4aa8fc706d01905c56af57759e078c53a6fddf4a13361943e7a" +dependencies = [ + "block", + "core-foundation 0.9.4", + "core-graphics 0.22.3", + "dispatch", + "lazy_static", + "libc", + "objc", + "objc_id", + "os_info", + "url", +] + +[[package]] +name = "cache_control" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2a5fb3207c12b5d208ebc145f967fea5cac41a021c37417ccc31ba40f39ee" + [[package]] name = "cairo-rs" version = "0.18.5" @@ -848,12 +1231,21 @@ checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.24", "serde", "serde_json", "thiserror 2.0.8", ] +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cc" version = "1.2.5" @@ -877,6 +1269,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + [[package]] name = "cfg-expr" version = "0.15.8" @@ -915,10 +1316,32 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets 0.52.6", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.6", +] + [[package]] name = "clap" version = "4.5.23" @@ -1008,6 +1431,27 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color-print" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" +dependencies = [ + "nom 7.1.3", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -1045,6 +1489,26 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + [[package]] name = "convert_case" version = "0.6.0" @@ -1063,6 +1527,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cooked-waker" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" + [[package]] name = "core-foundation" version = "0.9.4" @@ -1089,6 +1559,19 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types 0.3.2", + "libc", +] + [[package]] name = "core-graphics" version = "0.23.2" @@ -1098,7 +1581,7 @@ dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", "core-graphics-types 0.1.3", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -1111,7 +1594,7 @@ dependencies = [ "bitflags 2.6.0", "core-foundation 0.10.0", "core-graphics-types 0.2.0", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -1137,6 +1620,20 @@ dependencies = [ "libc", ] +[[package]] +name = "cosmic-protocols" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-protocols.git#d218c76b58c7a3b20dd5e7943f93fc306a1b81b8" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols 0.32.5", + "wayland-protocols-wlr 0.3.5", + "wayland-scanner", + "wayland-server", +] + [[package]] name = "cosmic-text" version = "0.12.1" @@ -1242,6 +1739,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1249,6 +1758,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] @@ -1258,12 +1768,59 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "cursor-icon" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto 0.2.9", + "rustc_version 0.4.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "d3d12" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" +dependencies = [ + "bitflags 2.6.0", + "libloading 0.8.6", + "winapi", +] + [[package]] name = "dark-light" version = "1.1.1" @@ -1274,9 +1831,9 @@ dependencies = [ "detect-desktop-environment", "dirs 4.0.0", "objc", - "rust-ini", + "rust-ini 0.18.0", "web-sys", - "winreg", + "winreg 0.10.1", "zbus", ] @@ -1315,6 +1872,25 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "data-url" version = "0.3.0" @@ -1327,6 +1903,907 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7046468a81e6a002061c01e6a7c83139daf91b11c30e66795b13217c2d885c8b" +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "deno_ast" +version = "0.43.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d00b724e06d2081a141ec1155756a0b465d413d8e2a7515221f61d482eb2ee" +dependencies = [ + "base64 0.21.7", + "deno_media_type", + "deno_terminal 0.1.1", + "dprint-swc-ext", + "once_cell", + "percent-encoding", + "serde", + "sourcemap 9.1.2", + "swc_atoms", + "swc_common", + "swc_config", + "swc_config_macro", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_codegen_macros", + "swc_ecma_loader", + "swc_ecma_parser", + "swc_ecma_transforms_base", + "swc_ecma_transforms_classes", + "swc_ecma_transforms_macros", + "swc_ecma_transforms_proposal", + "swc_ecma_transforms_react", + "swc_ecma_transforms_typescript", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_eq_ignore_macros", + "swc_macros_common", + "swc_visit", + "swc_visit_macros", + "text_lines", + "thiserror 1.0.69", + "unicode-width", + "url", +] + +[[package]] +name = "deno_broadcast_channel" +version = "0.173.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348ecdacfdd262e6b2f9740d07a41e8f4d79d06a670378a060515d0208495c9f" +dependencies = [ + "async-trait", + "deno_core", + "thiserror 1.0.69", + "tokio", + "uuid", +] + +[[package]] +name = "deno_cache" +version = "0.111.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6e35cb122e56c22149652327c90c563790ddcef24ea1fc77454e193131318e" +dependencies = [ + "async-trait", + "deno_core", + "rusqlite", + "serde", + "sha2", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "deno_canvas" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bbfd1437bc01ab775b1a60e3061bbf2e9517e31fb5eedf89b2b703104c835e6" +dependencies = [ + "deno_core", + "deno_webgpu", + "image 0.24.9", + "serde", + "thiserror 1.0.69", +] + +[[package]] +name = "deno_console" +version = "0.179.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e09f2bbb2d842329b602da25dbab5cd4a342f9a8adcb7c02509fc322f796e79" +dependencies = [ + "deno_core", +] + +[[package]] +name = "deno_core" +version = "0.321.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd2a54cda74cdc187d5fc2d23370a45cf09f912caf566dd1cd24a50157d809c7" +dependencies = [ + "anyhow", + "bincode 1.3.3", + "bit-set 0.5.3", + "bit-vec 0.6.3", + "bytes", + "cooked-waker", + "deno_core_icudata", + "deno_ops", + "deno_unsync", + "futures", + "indexmap 2.7.0", + "libc", + "memoffset", + "parking_lot 0.12.3", + "percent-encoding", + "pin-project", + "serde", + "serde_json", + "serde_v8", + "smallvec", + "sourcemap 8.0.1", + "static_assertions", + "tokio", + "url", + "v8", + "wasm_dep_analyzer", +] + +[[package]] +name = "deno_core_icudata" +version = "0.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" + +[[package]] +name = "deno_cron" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f936f036e9e3f88205db8efd0ec68c65efb47bc0cbe4b715bafecd6e9c407931" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "deno_core", + "saffron", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "deno_crypto" +version = "0.193.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b582f30887c7c0902b4445c64d7c8b98d0043ec547c44de8de26104b093e1be" +dependencies = [ + "aes", + "aes-gcm", + "aes-kw", + "base64 0.21.7", + "cbc", + "const-oid", + "ctr", + "curve25519-dalek", + "deno_core", + "deno_web", + "ed448-goldilocks", + "elliptic-curve", + "num-traits", + "once_cell", + "p256", + "p384", + "p521", + "rand", + "ring", + "rsa", + "sec1", + "serde", + "serde_bytes", + "sha1", + "sha2", + "signature", + "spki", + "thiserror 1.0.69", + "tokio", + "uuid", + "x25519-dalek", +] + +[[package]] +name = "deno_fetch" +version = "0.203.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18e66bd3bf786e24a8b8bdc97049fa82957b095a5fd1e142545c5a7cdd2272a" +dependencies = [ + "base64 0.21.7", + "bytes", + "data-url", + "deno_core", + "deno_permissions", + "deno_tls", + "dyn-clone", + "error_reporter", + "hickory-resolver", + "http 1.2.0", + "http-body-util", + "hyper 1.5.2", + "hyper-rustls", + "hyper-util", + "ipnet", + "percent-encoding", + "rustls-webpki", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tokio-rustls", + "tokio-socks", + "tokio-util", + "tower 0.4.13", + "tower-http", + "tower-service", +] + +[[package]] +name = "deno_ffi" +version = "0.166.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6d2f13ebfa93833446abeb3bd1836fdf86bcb96678276b21a0622146f42284" +dependencies = [ + "deno_core", + "deno_permissions", + "dlopen2 0.6.1", + "dynasmrt", + "libffi", + "libffi-sys", + "log", + "num-bigint", + "serde", + "serde-value", + "serde_json", + "thiserror 1.0.69", + "tokio", + "winapi", +] + +[[package]] +name = "deno_fs" +version = "0.89.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f53829328c344736d7fdda44733057299536f3379513cdcd258823ef273540ec" +dependencies = [ + "async-trait", + "base32", + "boxed_error", + "deno_core", + "deno_io", + "deno_path_util", + "deno_permissions", + "filetime", + "junction", + "libc", + "nix 0.27.1", + "rand", + "rayon", + "serde", + "thiserror 1.0.69", + "winapi", + "windows-sys 0.52.0", +] + +[[package]] +name = "deno_http" +version = "0.177.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b4ee6dbac20aa287a416f8905ed64b95cb484063c2af6be4eb232382c7fcb6" +dependencies = [ + "async-compression", + "async-trait", + "base64 0.21.7", + "brotli 6.0.0", + "bytes", + "cache_control", + "deno_core", + "deno_net", + "deno_websocket", + "flate2", + "http 0.2.12", + "http 1.2.0", + "httparse", + "hyper 0.14.32", + "hyper 1.5.2", + "hyper-util", + "itertools 0.10.5", + "memmem", + "mime", + "once_cell", + "percent-encoding", + "phf", + "pin-project", + "ring", + "scopeguard", + "serde", + "smallvec", + "thiserror 1.0.69", + "tokio", + "tokio-util", +] + +[[package]] +name = "deno_io" +version = "0.89.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc19195805a6b256d5ffe697c81ac79f8acd22246616fe880d6c9ec2dacf9bb4" +dependencies = [ + "async-trait", + "deno_core", + "filetime", + "fs3", + "libc", + "log", + "once_cell", + "os_pipe", + "parking_lot 0.12.3", + "pin-project", + "rand", + "tokio", + "uuid", + "winapi", + "windows-sys 0.52.0", +] + +[[package]] +name = "deno_kv" +version = "0.87.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a25347cd7ae561d0b05c24eebb3047e85a3af3f398675d5a9894fd167f2714f" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.21.7", + "boxed_error", + "bytes", + "chrono", + "deno_core", + "deno_fetch", + "deno_path_util", + "deno_permissions", + "deno_tls", + "denokv_proto", + "denokv_remote", + "denokv_sqlite", + "faster-hex", + "http 1.2.0", + "http-body-util", + "log", + "num-bigint", + "prost", + "prost-build", + "rand", + "rusqlite", + "serde", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "deno_media_type" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaa135b8a9febc9a51c16258e294e268a1276750780d69e46edb31cced2826e4" +dependencies = [ + "data-url", + "serde", + "url", +] + +[[package]] +name = "deno_napi" +version = "0.110.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea57b67488969f82594cb008fed1bd99830e6db042e31ee9878933d8c76be41c" +dependencies = [ + "deno_core", + "deno_permissions", + "libc", + "libloading 0.7.4", + "log", + "napi_sym", + "thiserror 1.0.69", + "windows-sys 0.52.0", +] + +[[package]] +name = "deno_native_certs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bc737e098a45aa5742d51ce694ac7236a1e69fb0d9df8c862e9b4c9583c5f9" +dependencies = [ + "dlopen2 0.7.0", + "dlopen2_derive", + "once_cell", + "rustls-native-certs", + "rustls-pemfile", +] + +[[package]] +name = "deno_net" +version = "0.171.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b3a51f7b4d5d64d17a7bc6f7495498f20d809930979d21a059d75e850cdea6" +dependencies = [ + "deno_core", + "deno_permissions", + "deno_tls", + "hickory-proto", + "hickory-resolver", + "pin-project", + "rustls-tokio-stream", + "serde", + "socket2", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "deno_node" +version = "0.116.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd0d1a757f75224e84ce8a553c2465e4a352fba4b7551ec15809d8a119847e7" +dependencies = [ + "aead-gcm-stream", + "aes", + "async-trait", + "base64 0.21.7", + "blake2", + "boxed_error", + "brotli 6.0.0", + "bytes", + "cbc", + "const-oid", + "data-encoding", + "deno_core", + "deno_fetch", + "deno_fs", + "deno_io", + "deno_media_type", + "deno_net", + "deno_package_json", + "deno_path_util", + "deno_permissions", + "deno_whoami", + "der", + "digest", + "dsa", + "ecb", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "errno 0.2.8", + "faster-hex", + "h2 0.4.7", + "hkdf", + "home", + "http 1.2.0", + "http-body-util", + "hyper 1.5.2", + "hyper-util", + "idna", + "indexmap 2.7.0", + "ipnetwork", + "k256", + "lazy-regex", + "libc", + "libz-sys", + "md-5", + "md4", + "memchr", + "node_resolver", + "num-bigint", + "num-bigint-dig", + "num-integer", + "num-traits", + "once_cell", + "p224", + "p256", + "p384", + "path-clean", + "pbkdf2", + "pin-project-lite", + "pkcs8", + "rand", + "regex", + "ring", + "ripemd", + "rsa", + "scrypt", + "sec1", + "serde", + "sha1", + "sha2", + "sha3", + "signature", + "simd-json", + "sm3", + "spki", + "stable_deref_trait", + "thiserror 1.0.69", + "tokio", + "tokio-eld", + "url", + "webpki-root-certs", + "winapi", + "windows-sys 0.52.0", + "x25519-dalek", + "x509-parser", + "yoke", +] + +[[package]] +name = "deno_ops" +version = "0.197.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a8825d92301cf445727c43f17fee2a20fcdf4370004339965156ae7c56c97e" +dependencies = [ + "proc-macro-rules", + "proc-macro2", + "quote", + "stringcase", + "strum", + "strum_macros", + "syn 2.0.101", + "thiserror 1.0.69", +] + +[[package]] +name = "deno_package_json" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbc4c4d3eb0960b58e8f43f9fc2d3f620fcac9a03cd85203e08db5b04e83c1f" +dependencies = [ + "deno_semver", + "indexmap 2.7.0", + "serde", + "serde_json", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "deno_path_util" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff25f6e08e7a0214bbacdd6f7195c7f1ebcd850c87a624e4ff06326b68b42d99" +dependencies = [ + "percent-encoding", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "deno_permissions" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e822f98185ab3ddf06104b2407681e0008af52361af32f1cd171b7eda5aa59" +dependencies = [ + "deno_core", + "deno_path_util", + "deno_terminal 0.2.0", + "fqdn", + "libc", + "log", + "once_cell", + "percent-encoding", + "serde", + "thiserror 1.0.69", + "which 4.4.2", + "winapi", +] + +[[package]] +name = "deno_runtime" +version = "0.188.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516ed4f796ab0f5dc092b5592ed6159c759f4f3a94f4a23455fecc94edc51dd1" +dependencies = [ + "async-trait", + "color-print", + "deno_ast", + "deno_broadcast_channel", + "deno_cache", + "deno_canvas", + "deno_console", + "deno_core", + "deno_cron", + "deno_crypto", + "deno_fetch", + "deno_ffi", + "deno_fs", + "deno_http", + "deno_io", + "deno_kv", + "deno_napi", + "deno_net", + "deno_node", + "deno_path_util", + "deno_permissions", + "deno_terminal 0.2.0", + "deno_tls", + "deno_url", + "deno_web", + "deno_webgpu", + "deno_webidl", + "deno_websocket", + "deno_webstorage", + "dlopen2 0.6.1", + "encoding_rs", + "fastwebsockets", + "flate2", + "http 1.2.0", + "http-body-util", + "hyper 0.14.32", + "hyper 1.5.2", + "hyper-util", + "libc", + "log", + "netif", + "nix 0.27.1", + "node_resolver", + "notify", + "ntapi", + "once_cell", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", + "percent-encoding", + "pin-project", + "regex", + "rustyline", + "same-file", + "serde", + "signal-hook", + "signal-hook-registry", + "tempfile", + "thiserror 1.0.69", + "tokio", + "tokio-metrics", + "twox-hash", + "uuid", + "which 4.4.2", + "winapi", + "windows-sys 0.52.0", +] + +[[package]] +name = "deno_semver" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c957c6a57c38b7dde2315df0da0ec228911e56a74f185b108a488d0401841a67" +dependencies = [ + "monch", + "once_cell", + "serde", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "deno_terminal" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e6337d4e7f375f8b986409a76fbeecfa4bd8a1343e63355729ae4befa058eaf" +dependencies = [ + "once_cell", + "termcolor", +] + +[[package]] +name = "deno_terminal" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daef12499e89ee99e51ad6000a91f600d3937fb028ad4918af76810c5bc9e0d5" +dependencies = [ + "once_cell", + "termcolor", +] + +[[package]] +name = "deno_tls" +version = "0.166.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688175eed35e7b3053ec114227894ef24786855405d8844058a48bffa997d85a" +dependencies = [ + "deno_core", + "deno_native_certs", + "rustls 0.23.20", + "rustls-pemfile", + "rustls-tokio-stream", + "rustls-webpki", + "serde", + "thiserror 1.0.69", + "tokio", + "webpki-roots", +] + +[[package]] +name = "deno_unsync" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d774fd83f26b24f0805a6ab8b26834a0d06ceac0db517b769b1e4633c96a2057" +dependencies = [ + "futures", + "parking_lot 0.12.3", + "tokio", +] + +[[package]] +name = "deno_url" +version = "0.179.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9a108794e505f2b07665e19ff336c1bcba6adcf7182c90c1d3a6c741d7fcd0" +dependencies = [ + "deno_core", + "thiserror 1.0.69", + "urlpattern", +] + +[[package]] +name = "deno_web" +version = "0.210.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7679087bcc41f7ae3385f8c12d43bc81cfc54cb9b1ef73983d20f5e39fa4e0da" +dependencies = [ + "async-trait", + "base64-simd 0.8.0", + "bytes", + "deno_core", + "deno_permissions", + "encoding_rs", + "flate2", + "futures", + "serde", + "thiserror 1.0.69", + "tokio", + "uuid", +] + +[[package]] +name = "deno_webgpu" +version = "0.146.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48f78b73638be1552b31778e42267f4fb47e902f7b261bdb0f951ba2b1d6bfab" +dependencies = [ + "deno_core", + "raw-window-handle", + "serde", + "thiserror 1.0.69", + "tokio", + "wgpu-core 0.21.1", + "wgpu-types 0.20.0", +] + +[[package]] +name = "deno_webidl" +version = "0.179.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b55d845e3d64f8de7eff67aaa4b6fe1b23bbc2efe967c984f8c64c8dd85fad4" +dependencies = [ + "deno_core", +] + +[[package]] +name = "deno_websocket" +version = "0.184.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00407052c6524828f2708557c47059ba9b87874758416c66f47f5102ac68422" +dependencies = [ + "bytes", + "deno_core", + "deno_net", + "deno_permissions", + "deno_tls", + "fastwebsockets", + "h2 0.4.7", + "http 1.2.0", + "http-body-util", + "hyper 1.5.2", + "hyper-util", + "once_cell", + "rustls-tokio-stream", + "serde", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "deno_webstorage" +version = "0.174.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecaabbb1580d21811642f11cc12fe8599684efeb9398eaa998a3db8811e8edc" +dependencies = [ + "deno_core", + "deno_web", + "rusqlite", + "thiserror 1.0.69", +] + +[[package]] +name = "deno_whoami" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75e4caa92b98a27f09c671d1399aee0f5970aa491b9a598523aac000a2192e3" +dependencies = [ + "libc", + "whoami", +] + +[[package]] +name = "denokv_proto" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7ba1f99ed11a9c11e868a8521b1f71a7e1aba785d7f42ea9ecbdc01146c89ec" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "futures", + "num-bigint", + "prost", + "serde", + "uuid", +] + +[[package]] +name = "denokv_remote" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ed833073189e8f6d03155fe3b05a024e75e29d8a28a4c2e9ec3b5c925e727b" +dependencies = [ + "anyhow", + "async-stream", + "async-trait", + "bytes", + "chrono", + "denokv_proto", + "futures", + "http 1.2.0", + "log", + "prost", + "rand", + "serde", + "serde_json", + "tokio", + "tokio-util", + "url", + "uuid", +] + +[[package]] +name = "denokv_sqlite" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b790f01d1302d53a0c3cbd27de88a06b3abd64ec8ab8673924e490541c7c713" +dependencies = [ + "anyhow", + "async-stream", + "async-trait", + "chrono", + "denokv_proto", + "futures", + "hex", + "log", + "num-bigint", + "rand", + "rusqlite", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tokio-stream", + "uuid", + "v8_valueserializer", +] + [[package]] name = "der" version = "0.7.9" @@ -1334,10 +2811,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", + "der_derive", "pem-rfc7468", "zeroize", ] +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom 7.1.3", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1494,12 +2997,56 @@ dependencies = [ "libloading 0.8.6", ] +[[package]] +name = "dlopen2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bc2c7ed06fd72a8513ded8d0d2f6fd2655a85d6885c48cae8625d80faf28c03" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "dlv-list" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + [[package]] name = "doctest-file" version = "1.0.0" @@ -1533,6 +3080,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +[[package]] +name = "dprint-swc-ext" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba28c12892aadb751c2ba7001d8460faee4748a04b4edc51c7121cc67ee03db" +dependencies = [ + "num-bigint", + "rustc-hash 1.1.0", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "text_lines", +] + [[package]] name = "drm" version = "0.12.0" @@ -1572,12 +3134,115 @@ dependencies = [ "linux-raw-sys 0.6.5", ] +[[package]] +name = "dsa" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" +dependencies = [ + "digest", + "num-bigint-dig", + "num-traits", + "pkcs8", + "rfc6979", + "sha2", + "signature", + "zeroize", +] + [[package]] name = "dyn-clone" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2 0.5.10", +] + +[[package]] +name = "ecb" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" +dependencies = [ + "cipher", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "signature", + "subtle", + "zeroize", +] + +[[package]] +name = "ed448-goldilocks" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06924531e9e90130842b012e447f85bdaf9161bc8a0f8092be8cb70b01ebe092" +dependencies = [ + "fiat-crypto 0.1.20", + "hex", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.13.0" @@ -1587,12 +3252,127 @@ dependencies = [ "serde", ] +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "base64ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "serde_json", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" +dependencies = [ + "encoding-index-japanese", + "encoding-index-korean", + "encoding-index-simpchinese", + "encoding-index-singlebyte", + "encoding-index-tradchinese", +] + +[[package]] +name = "encoding-index-japanese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-korean" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-simpchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-singlebyte" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-tradchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding_index_tests" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "endi" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "enumflags2" version = "0.7.10" @@ -1614,12 +3394,39 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + [[package]] name = "errno" version = "0.3.10" @@ -1630,12 +3437,28 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "error-code" version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" +[[package]] +name = "error_reporter" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ae425815400e5ed474178a7a22e275a9687086a12ca63ec793ff292d8fdae8" + [[package]] name = "etagere" version = "0.2.13" @@ -1696,12 +3519,24 @@ dependencies = [ "bit_field", "half", "lebe", - "miniz_oxide", + "miniz_oxide 0.8.2", "rayon-core", "smallvec", "zune-inflate", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fast-srgb8" version = "1.0.0" @@ -1729,6 +3564,37 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fastwebsockets" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26da0c7b5cef45c521a6f9cdfffdfeb6c9f5804fbac332deb5ae254634c7a6be" +dependencies = [ + "base64 0.21.7", + "bytes", + "http-body-util", + "hyper 1.5.2", + "hyper-util", + "pin-project", + "rand", + "sha1", + "simdutf8", + "thiserror 1.0.69", + "tokio", + "utf-8", +] + +[[package]] +name = "fd-lock" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "fdeflate" version = "0.3.7" @@ -1738,6 +3604,28 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "field-offset" version = "0.3.6" @@ -1745,7 +3633,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ "memoffset", - "rustc_version", + "rustc_version 0.4.1", +] + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", ] [[package]] @@ -1761,7 +3661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.2", ] [[package]] @@ -1770,6 +3670,15 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + [[package]] name = "flume" version = "0.11.1" @@ -1819,7 +3728,7 @@ checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" dependencies = [ "fontconfig-parser", "log", - "memmap2", + "memmap2 0.9.5", "slotmap", "tinyvec", "ttf-parser 0.20.0", @@ -1833,12 +3742,21 @@ checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" dependencies = [ "fontconfig-parser", "log", - "memmap2", + "memmap2 0.9.5", "slotmap", "tinyvec", "ttf-parser 0.21.1", ] +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -1846,7 +3764,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -1860,6 +3778,12 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1875,6 +3799,57 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fqdn" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb540cf7bc4fe6df9d8f7f0c974cfd0dce8ed4e9e8884e73433b503ee78b4e7d" + +[[package]] +name = "freedesktop-icons" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8ef34245e0540c9a3ce7a28340b98d2c12b75da0d446da4e8224923fcaa0c16" +dependencies = [ + "dirs 5.0.1", + "once_cell", + "rust-ini 0.20.0", + "thiserror 1.0.69", + "xdg", +] + +[[package]] +name = "freedesktop_entry_parser" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db9c27b72f19a99a895f8ca89e2d26e4ef31013376e56fdafef697627306c3e4" +dependencies = [ + "nom 7.1.3", + "thiserror 1.0.69", +] + +[[package]] +name = "from_variant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" +dependencies = [ + "proc-macro2", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "fs3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb17cf6ed704f72485332f6ab65257460c4f9f3083934cf402bf9f5b3b600a90" +dependencies = [ + "libc", + "rustc_version 0.2.3", + "winapi", +] + [[package]] name = "fs4" version = "0.8.4" @@ -1885,6 +3860,31 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "fslock" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.31" @@ -2053,8 +4053,8 @@ name = "gauntlet-common" version = "0.0.0" dependencies = [ "anyhow", - "base64", - "bincode", + "base64 0.22.1", + "bincode 2.0.0-rc.3", "bytes", "convert_case 0.6.0", "directories", @@ -2079,7 +4079,7 @@ name = "gauntlet-common-plugin-runtime" version = "0.1.0" dependencies = [ "anyhow", - "bincode", + "bincode 2.0.0-rc.3", "gauntlet-common", "gauntlet-utils", "gauntlet-utils-macros", @@ -2137,6 +4137,55 @@ dependencies = [ "serde_json", ] +[[package]] +name = "gauntlet-plugin-runtime" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode 2.0.0-rc.3", + "bytes", + "cacao", + "cosmic-protocols", + "deno_core", + "deno_runtime", + "encoding", + "freedesktop-icons", + "freedesktop_entry_parser", + "futures", + "gauntlet-common", + "gauntlet-common-plugin-runtime", + "gauntlet-component-model", + "gauntlet-utils", + "icns", + "image 0.25.5", + "indexmap 2.7.0", + "interprocess", + "libc", + "numbat", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", + "once_cell", + "open", + "plist", + "regex", + "resvg 0.44.0", + "serde", + "smithay-client-toolkit", + "sys-locale", + "tokio", + "tokio-util", + "tracing", + "typed-path", + "uuid", + "walkdir", + "wayland-client", + "wayland-protocols-wlr 0.3.5", + "which 7.0.1", + "windows", + "x11rb", +] + [[package]] name = "gauntlet-scenario-runner" version = "0.0.0" @@ -2161,6 +4210,7 @@ dependencies = [ "gauntlet-client", "gauntlet-common", "gauntlet-common-plugin-runtime", + "gauntlet-plugin-runtime", "gauntlet-scenario-runner", "gauntlet-utils", "git2", @@ -2277,6 +4327,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -2296,8 +4347,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", ] [[package]] @@ -2480,6 +4543,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "global-hotkey" version = "0.7.0" @@ -2497,6 +4566,18 @@ dependencies = [ "xkeysym", ] +[[package]] +name = "glow" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "glow" version = "0.14.2" @@ -2509,6 +4590,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "glutin_wgl_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +dependencies = [ + "gl_generator", +] + [[package]] name = "glutin_wgl_sys" version = "0.6.0" @@ -2592,6 +4682,17 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "gtk" version = "0.18.2" @@ -2654,6 +4755,34 @@ dependencies = [ "svg_fmt", ] +[[package]] +name = "gzip-header" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" +dependencies = [ + "crc32fast", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.7.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.7" @@ -2665,7 +4794,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.2.0", "indexmap 2.7.0", "slab", "tokio", @@ -2683,6 +4812,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "halfbrown" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8588661a8607108a5ca69cab034063441a0413a0b041c13618a7dd348021ef6f" +dependencies = [ + "hashbrown 0.14.5", + "serde", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2722,11 +4861,28 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "base64 0.21.7", + "byteorder", + "crossbeam-channel", + "flate2", + "nom 7.1.3", + "num-traits", +] + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "heck" @@ -2758,6 +4914,53 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "hickory-proto" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna", + "ipnet", + "once_cell", + "rand", + "serde", + "thiserror 1.0.69", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot 0.12.3", + "rand", + "resolv-conf", + "serde", + "smallvec", + "thiserror 1.0.69", + "tokio", + "tracing", +] + [[package]] name = "hkdf" version = "0.12.4" @@ -2785,12 +4988,48 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "hstr" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412" +dependencies = [ + "hashbrown 0.14.5", + "new_debug_unreachable", + "once_cell", + "phf", + "rustc-hash 1.1.0", + "triomphe", +] + [[package]] name = "htmlescape" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.2.0" @@ -2802,6 +5041,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -2809,7 +5059,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.2.0", ] [[package]] @@ -2820,8 +5070,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.2.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -2837,6 +5087,39 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.5.2" @@ -2846,9 +5129,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -2858,13 +5141,30 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6884a48c6826ec44f524c7456b163cebe9e55a18d7b5e307cb4f100371cc767" +dependencies = [ + "futures-util", + "http 1.2.0", + "hyper 1.5.2", + "hyper-util", + "rustls 0.23.20", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-timeout" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper", + "hyper 1.5.2", "hyper-util", "pin-project-lite", "tokio", @@ -2880,9 +5180,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.5.2", "pin-project-lite", "socket2", "tokio", @@ -3078,7 +5378,7 @@ dependencies = [ "iced_graphics", "kurbo 0.10.4", "log", - "resvg", + "resvg 0.42.0", "rustc-hash 2.1.0", "softbuffer", "tiny-skia", @@ -3098,7 +5398,7 @@ dependencies = [ "iced_graphics", "log", "once_cell", - "resvg", + "resvg 0.42.0", "rustc-hash 2.1.0", "thiserror 1.0.69", "wgpu", @@ -3137,6 +5437,16 @@ dependencies = [ "winit", ] +[[package]] +name = "icns" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ccfbad7e08da70a5b48a924994a5afd93125ce5d45a3b0ba0b8da7bda59a40" +dependencies = [ + "byteorder", + "png 0.16.8", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -3282,6 +5592,12 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + [[package]] name = "image" version = "0.24.9" @@ -3295,7 +5611,7 @@ dependencies = [ "gif", "jpeg-decoder", "num-traits", - "png", + "png 0.17.16", "qoi", "tiff", ] @@ -3313,7 +5629,7 @@ dependencies = [ "gif", "image-webp", "num-traits", - "png", + "png 0.17.16", "qoi", "ravif", "rayon", @@ -3330,7 +5646,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" dependencies = [ "byteorder-lite", - "quick-error", + "quick-error 2.0.1", ] [[package]] @@ -3339,6 +5655,12 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + [[package]] name = "imgref" version = "1.11.0" @@ -3372,6 +5694,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -3385,6 +5708,36 @@ dependencies = [ "serde", ] +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + [[package]] name = "instant" version = "0.1.13" @@ -3423,6 +5776,39 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "io-lifetimes" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2", + "widestring", + "windows-sys 0.48.0", + "winreg 0.50.0", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] + [[package]] name = "is-docker" version = "0.2.0" @@ -3432,6 +5818,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "is-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "is-wsl" version = "0.4.0" @@ -3448,6 +5846,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -3472,6 +5879,33 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +[[package]] +name = "jiff" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db69f08d4fb10524cacdb074c10b296299d71274ddbc830a8ee65666867002e9" +dependencies = [ + "jiff-tzdb-platform", + "js-sys", + "wasm-bindgen", + "windows-sys 0.59.0", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91335e575850c5c4c673b9bd467b0e025f164ca59d0564f69d0c2ee0ffad4653" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9835f0060a626fe59f160437bc725491a6af23133ea906500027d1bd2f8f4329" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jni" version = "0.21.1" @@ -3522,6 +5956,30 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "junction" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be39922b087cecaba4e2d5592dedfc8bda5d4a5a1231f143337cca207950b61d" +dependencies = [ + "scopeguard", + "winapi", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + [[package]] name = "kamadak-exif" version = "0.5.5" @@ -3531,6 +5989,15 @@ dependencies = [ "mutate_once", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "keyboard-types" version = "0.7.0" @@ -3559,6 +6026,26 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "kurbo" version = "0.10.4" @@ -3600,6 +6087,29 @@ dependencies = [ "wayland-protocols-wlr 0.3.5", ] +[[package]] +name = "lazy-regex" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d8e41c97e6bc7ecb552016274b99fbb5d035e8de288c582d9b933af6677bfda" +dependencies = [ + "lazy-regex-proc_macros", + "once_cell", + "regex", +] + +[[package]] +name = "lazy-regex-proc_macros" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e1d8b05d672c53cb9c7b920bbba8783845ae4f0b076e02a3db1d02c81b4163" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.101", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -3651,6 +6161,24 @@ version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +[[package]] +name = "libffi" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2" +dependencies = [ + "libc", + "libffi-sys", +] + +[[package]] +name = "libffi-sys" +version = "2.3.0" +source = "git+https://github.com/tov/libffi-rs?rev=d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b#d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b" +dependencies = [ + "cc", +] + [[package]] name = "libfuzzer-sys" version = "0.4.8" @@ -3749,6 +6277,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -3807,6 +6341,15 @@ dependencies = [ "hashbrown 0.15.2", ] +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "lz4_flex" version = "0.11.3" @@ -3846,6 +6389,12 @@ dependencies = [ "quote", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -3881,6 +6430,15 @@ dependencies = [ "digest", ] +[[package]] +name = "md4" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da5ac363534dce5fabf69949225e174fbf111a498bf0ff794c8ea1fba9f3dda" +dependencies = [ + "digest", +] + [[package]] name = "measure_time" version = "0.8.3" @@ -3897,6 +6455,24 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + [[package]] name = "memmap2" version = "0.9.5" @@ -3906,6 +6482,12 @@ dependencies = [ "libc", ] +[[package]] +name = "memmem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + [[package]] name = "memoffset" version = "0.9.1" @@ -3915,6 +6497,30 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mendeleev" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8dd6ec5207f7f69db7abb42466511394956dc85faf163de1fe393246c8b7e4" +dependencies = [ + "serde", +] + +[[package]] +name = "metal" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" +dependencies = [ + "bitflags 2.6.0", + "block", + "core-graphics-types 0.1.3", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + [[package]] name = "metal" version = "0.29.0" @@ -3924,7 +6530,7 @@ dependencies = [ "bitflags 2.6.0", "block", "core-graphics-types 0.1.3", - "foreign-types", + "foreign-types 0.5.0", "log", "objc", "paste", @@ -3936,12 +6542,40 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + [[package]] name = "miniz_oxide" version = "0.8.2" @@ -3952,6 +6586,18 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "mio" version = "1.0.3" @@ -3963,6 +6609,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "monch" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52c1b33ff98142aecea13138bd399b68aa7ab5d9546c300988c345004001eea" + [[package]] name = "muda" version = "0.15.3" @@ -3977,7 +6629,7 @@ dependencies = [ "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", "once_cell", - "png", + "png 0.17.16", "thiserror 1.0.69", "windows-sys 0.59.0", ] @@ -4000,6 +6652,28 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" +[[package]] +name = "naga" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" +dependencies = [ + "arrayvec", + "bit-set 0.5.3", + "bitflags 2.6.0", + "codespan-reporting", + "hexf-parse", + "indexmap 2.7.0", + "log", + "num-traits", + "rustc-hash 1.1.0", + "serde", + "spirv", + "termcolor", + "thiserror 1.0.69", + "unicode-xid", +] + [[package]] name = "naga" version = "23.1.0" @@ -4007,7 +6681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f" dependencies = [ "arrayvec", - "bit-set", + "bit-set 0.8.0", "bitflags 2.6.0", "cfg_aliases 0.1.1", "codespan-reporting", @@ -4021,6 +6695,18 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "napi_sym" +version = "0.109.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b3ee1b2d30885de3ee82429b5aebe6f22b3eae5cb290cd8d6537a62212812b" +dependencies = [ + "quote", + "serde", + "serde_json", + "syn 2.0.101", +] + [[package]] name = "ndk" version = "0.9.0" @@ -4060,12 +6746,42 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "netif" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29a01b9f018d6b7b277fef6c79fdbd9bf17bb2d1e298238055cafab49baa5ee" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "libc", +] + [[package]] name = "nix" version = "0.28.0" @@ -4091,6 +6807,39 @@ dependencies = [ "memoffset", ] +[[package]] +name = "node_resolver" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e999e1cdbb49cdfa3f63ddd061c57205aa5f7be8f43bdbc4081c0f60d24d7d" +dependencies = [ + "anyhow", + "async-trait", + "boxed_error", + "deno_media_type", + "deno_package_json", + "deno_path_util", + "futures", + "lazy-regex", + "once_cell", + "path-clean", + "regex", + "serde_json", + "thiserror 1.0.69", + "tokio", + "url", +] + +[[package]] +name = "nom" +version = "5.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" +dependencies = [ + "memchr", + "version_check", +] + [[package]] name = "nom" version = "7.1.3" @@ -4107,6 +6856,34 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.6.0", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio 0.8.11", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -4125,6 +6902,8 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", + "rand", + "serde", ] [[package]] @@ -4140,6 +6919,7 @@ dependencies = [ "num-iter", "num-traits", "rand", + "serde", "smallvec", "zeroize", ] @@ -4252,6 +7032,46 @@ dependencies = [ "libc", ] +[[package]] +name = "numbat" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5124c7a716bd197d4ad501237fa890771f69f38b34eb87f4514fdebf0cdcaf5b" +dependencies = [ + "codespan-reporting", + "heck 0.4.1", + "indexmap 2.7.0", + "itertools 0.12.1", + "jiff", + "libc", + "mendeleev", + "num-format", + "num-integer", + "num-rational", + "num-traits", + "numbat-exchange-rates", + "plotly", + "pretty_dtoa", + "rand", + "rust-embed", + "strfmt", + "strsim", + "thiserror 1.0.69", + "unicode-ident", + "unicode-width", + "walkdir", +] + +[[package]] +name = "numbat-exchange-rates" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd1e3c3e4f9f22d0d7cdcb413f01194f6506a302a9029d95deedcd1c25df7718" +dependencies = [ + "attohttpc", + "quick-xml 0.31.0", +] + [[package]] name = "objc" version = "0.2.7" @@ -4506,6 +7326,15 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "object" version = "0.36.5" @@ -4515,6 +7344,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.20.2" @@ -4527,6 +7365,12 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e296cf87e61c9cfc1a61c3c63a0f7f286ed4554e0e22be84e8a38e1d264a2a29" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "open" version = "5.3.1" @@ -4566,6 +7410,92 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opentelemetry" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab70038c28ed37b97d8ed414b6429d343a8bbf44c9f79ec854f3a643029ba6d7" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 1.0.69", + "tracing", +] + +[[package]] +name = "opentelemetry-http" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a8a7f5f6ba7c1b286c2fbca0454eaba116f63bbe69ed250b642d36fbb04d80" +dependencies = [ + "async-trait", + "bytes", + "http 1.2.0", + "opentelemetry", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76" +dependencies = [ + "async-trait", + "futures-core", + "http 1.2.0", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tonic", + "tracing", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e05acbfada5ec79023c85368af14abd0b307c015e9064d249b2a950ef459a6" +dependencies = [ + "hex", + "opentelemetry", + "opentelemetry_sdk", + "prost", + "serde", + "tonic", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc1b6902ff63b32ef6c489e8048c5e253e2e4a803ea3ea7e783914536eb15c52" + +[[package]] +name = "opentelemetry_sdk" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8" +dependencies = [ + "async-trait", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "opentelemetry", + "percent-encoding", + "rand", + "serde_json", + "thiserror 1.0.69", + "tracing", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -4581,16 +7511,35 @@ dependencies = [ "libredox", ] +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-multimap" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" dependencies = [ - "dlv-list", + "dlv-list 0.3.0", "hashbrown 0.12.3", ] +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list 0.5.2", + "hashbrown 0.14.5", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -4601,6 +7550,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_info" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ca711d8b83edbb00b44d504503cd247c9c0bd8b0fa2694f2a1a3d8165379ce" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "os_pipe" version = "1.1.5" @@ -4611,6 +7571,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "outref" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + [[package]] name = "overload" version = "0.1.1" @@ -4635,6 +7607,56 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "p224" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c06436d66652bc2f01ade021592c80a2aad401570a18aa18b82e440d2b9aa1" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core", + "sha2", +] + [[package]] name = "palette" version = "0.7.6" @@ -4738,18 +7760,45 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "path-clean" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" + [[package]] name = "pathdiff" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -4877,6 +7926,21 @@ dependencies = [ "spki", ] +[[package]] +name = "pkcs5" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" +dependencies = [ + "aes", + "cbc", + "der", + "pbkdf2", + "scrypt", + "sha2", + "spki", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -4884,6 +7948,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", + "pkcs5", + "rand_core", "spki", ] @@ -4893,6 +7959,61 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "plist" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +dependencies = [ + "base64 0.22.1", + "indexmap 2.7.0", + "quick-xml 0.32.0", + "serde", + "time", +] + +[[package]] +name = "plotly" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e1ffd11c8a6ef0b730b9d3e46ad2404f79905825cb20223fa0547434a2dff54" +dependencies = [ + "dyn-clone", + "erased-serde", + "once_cell", + "plotly_derive", + "rand", + "rinja", + "serde", + "serde_json", + "serde_repr", + "serde_with", +] + +[[package]] +name = "plotly_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e940d8d8db30c6f4cc37dab9aab61f4c9cc1e6efb6d18902ab88fa09c03560" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + [[package]] name = "png" version = "0.17.16" @@ -4903,7 +8024,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.8.2", ] [[package]] @@ -4921,6 +8042,18 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -4942,6 +8075,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" +[[package]] +name = "pretty_dtoa" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a239bcdfda2c685fda1add3b4695c06225f50075e3cfb5b954e91545587edff2" +dependencies = [ + "ryu_floating_decimal", +] + [[package]] name = "prettyplease" version = "0.2.25" @@ -4952,6 +8094,15 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -5004,6 +8155,29 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-rules" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" +dependencies = [ + "proc-macro-rules-macros", + "proc-macro2", + "syn 2.0.101", +] + +[[package]] +name = "proc-macro-rules-macros" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "207fffb0fe655d1d47f6af98cc2793405e85929bdbc420d685554ff07be27ac7" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "proc-macro-utils" version = "0.10.0" @@ -5095,6 +8269,35 @@ dependencies = [ "prost", ] +[[package]] +name = "psm" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" +dependencies = [ + "cc", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "qoi" version = "0.4.1" @@ -5104,12 +8307,36 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quick-error" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + [[package]] name = "quick-xml" version = "0.36.2" @@ -5128,6 +8355,22 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -5224,7 +8467,7 @@ dependencies = [ "avif-serialize", "imgref", "loop9", - "quick-error", + "quick-error 2.0.1", "rav1e", "rayon", "rgb", @@ -5310,6 +8553,26 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "regex" version = "1.11.1" @@ -5360,6 +8623,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error 1.2.3", +] + [[package]] name = "resvg" version = "0.42.0" @@ -5373,7 +8646,31 @@ dependencies = [ "rgb", "svgtypes", "tiny-skia", - "usvg", + "usvg 0.42.0", +] + +[[package]] +name = "resvg" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958" +dependencies = [ + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg 0.44.0", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", ] [[package]] @@ -5400,6 +8697,70 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rinja" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5" +dependencies = [ + "humansize", + "itoa", + "percent-encoding", + "rinja_derive", + "serde", + "serde_json", +] + +[[package]] +name = "rinja_derive" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b" +dependencies = [ + "basic-toml", + "memchr", + "mime", + "mime_guess", + "proc-macro2", + "quote", + "rinja_parser", + "rustc-hash 2.1.0", + "serde", + "syn 2.0.101", +] + +[[package]] +name = "rinja_parser" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610" +dependencies = [ + "memchr", + "nom 7.1.3", + "serde", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64 0.21.7", + "bitflags 2.6.0", + "serde", + "serde_derive", +] + [[package]] name = "roxmltree" version = "0.20.0" @@ -5426,6 +8787,55 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags 2.6.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rust-embed" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "shellexpand", + "syn 2.0.101", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +dependencies = [ + "sha2", + "walkdir", +] + [[package]] name = "rust-ini" version = "0.18.0" @@ -5433,7 +8843,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" dependencies = [ "cfg-if", - "ordered-multimap", + "ordered-multimap 0.4.3", +] + +[[package]] +name = "rust-ini" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" +dependencies = [ + "cfg-if", + "ordered-multimap 0.7.3", ] [[package]] @@ -5464,13 +8884,31 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.24", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom 7.1.3", ] [[package]] @@ -5480,12 +8918,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", - "errno", + "errno 0.3.10", "libc", "linux-raw-sys 0.4.14", "windows-sys 0.59.0", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls" version = "0.23.20" @@ -5501,12 +8953,46 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +[[package]] +name = "rustls-tokio-stream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22557157d7395bc30727745b365d923f1ecc230c4c80b176545f3f4f08c46e33" +dependencies = [ + "futures", + "rustls 0.23.20", + "socket2", + "tokio", +] + [[package]] name = "rustls-webpki" version = "0.102.8" @@ -5541,12 +9027,65 @@ dependencies = [ "unicode-script", ] +[[package]] +name = "rustyline" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix 0.27.1", + "radix_trie", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi", +] + [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "ryu-js" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" + +[[package]] +name = "ryu_floating_decimal" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "700de91d5fd6091442d00fdd9ee790af6d4f0f480562b0f5a1e8f59e90aafe73" + +[[package]] +name = "saffron" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03fb9a628596fc7590eb7edbf7b0613287be78df107f5f97b118aad59fb2eea9" +dependencies = [ + "chrono", + "nom 5.1.3", +] + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + [[package]] name = "same-file" version = "1.0.6" @@ -5556,6 +9095,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "schemars" version = "0.8.22" @@ -5592,6 +9140,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "password-hash", + "pbkdf2", + "salsa20", + "sha2", +] + [[package]] name = "sctk-adwaita" version = "0.10.1" @@ -5600,17 +9160,64 @@ checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" dependencies = [ "ab_glyph", "log", - "memmap2", + "memmap2 0.9.5", "smithay-client-toolkit", "tiny-skia", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "self_cell" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.24" @@ -5620,6 +9227,12 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.216" @@ -5629,6 +9242,25 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.216" @@ -5657,6 +9289,7 @@ version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ + "indexmap 2.7.0", "itoa", "memchr", "ryu", @@ -5695,6 +9328,59 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_v8" +version = "0.230.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a783242d2af51d6955cc04bf2b64adb643ab588b61e9573c908a69dabf8c2f" +dependencies = [ + "num-bigint", + "serde", + "smallvec", + "thiserror 1.0.69", + "v8", +] + +[[package]] +name = "serde_with" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -5717,6 +9403,16 @@ dependencies = [ "digest", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -5726,12 +9422,31 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shellexpand" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +dependencies = [ + "dirs 5.0.1", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -5751,12 +9466,36 @@ dependencies = [ "rand_core", ] +[[package]] +name = "simd-abstraction" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" +dependencies = [ + "outref 0.1.0", +] + [[package]] name = "simd-adler32" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd-json" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2bcf6c6e164e81bc7a5d49fc6988b3d515d9e8c07457d7b74ffb9324b9cd40" +dependencies = [ + "getrandom", + "halfbrown", + "ref-cast", + "serde", + "serde_json", + "simdutf8", + "value-trait", +] + [[package]] name = "simd_helpers" version = "0.1.0" @@ -5766,6 +9505,12 @@ dependencies = [ "quote", ] +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "simplecss" version = "0.2.1" @@ -5824,6 +9569,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "sm3" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebb9a3b702d0a7e33bc4d85a14456633d2b165c2ad839c5fd9a8417c1ab15860" +dependencies = [ + "digest", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -5833,6 +9587,17 @@ dependencies = [ "serde", ] +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + [[package]] name = "smithay-client-toolkit" version = "0.19.2" @@ -5840,12 +9605,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ "bitflags 2.6.0", + "bytemuck", "calloop 0.13.0", "calloop-wayland-source 0.3.0", "cursor-icon", "libc", "log", - "memmap2", + "memmap2 0.9.5", + "pkg-config", "rustix", "thiserror 1.0.69", "wayland-backend", @@ -5855,6 +9622,7 @@ dependencies = [ "wayland-protocols 0.32.5", "wayland-protocols-wlr 0.3.5", "wayland-scanner", + "xkbcommon", "xkeysym", ] @@ -5900,10 +9668,10 @@ dependencies = [ "core-graphics 0.24.0", "drm", "fastrand", - "foreign-types", + "foreign-types 0.5.0", "js-sys", "log", - "memmap2", + "memmap2 0.9.5", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-quartz-core", @@ -5920,6 +9688,44 @@ dependencies = [ "x11rb", ] +[[package]] +name = "sourcemap" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" +dependencies = [ + "base64-simd 0.7.0", + "bitvec", + "data-encoding", + "debugid", + "if_chain", + "rustc-hash 1.1.0", + "rustc_version 0.2.3", + "serde", + "serde_json", + "unicode-id-start", + "url", +] + +[[package]] +name = "sourcemap" +version = "9.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c4ea7042fd1a155ad95335b5d505ab00d5124ea0332a06c8390d200bb1a76a" +dependencies = [ + "base64-simd 0.7.0", + "bitvec", + "data-encoding", + "debugid", + "if_chain", + "rustc-hash 1.1.0", + "rustc_version 0.2.3", + "serde", + "serde_json", + "unicode-id-start", + "url", +] + [[package]] name = "spin" version = "0.9.8" @@ -5954,7 +9760,7 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" dependencies = [ - "nom", + "nom 7.1.3", "unicode_categories", ] @@ -6056,7 +9862,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags 2.6.0", "byteorder", "bytes", @@ -6098,7 +9904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags 2.6.0", "byteorder", "crc", @@ -6158,21 +9964,58 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stacker" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strfmt" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a8348af2d9fc3258c8733b8d9d8db2e56f54b2363a4b5b81585c7875ed65e65" + [[package]] name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" dependencies = [ - "float-cmp", + "float-cmp 0.9.0", ] +[[package]] +name = "string_enum" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "stringcase" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04028eeb851ed08af6aba5caa29f2d59a13ed168cee4d6bd753aeefcf1d636b0" + [[package]] name = "stringprep" version = "0.1.5" @@ -6190,6 +10033,28 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.101", +] + [[package]] name = "subtle" version = "2.6.1" @@ -6223,6 +10088,373 @@ dependencies = [ "zeno", ] +[[package]] +name = "swc_allocator" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76aa0eb65c0f39f9b6d82a7e5192c30f7ac9a78f084a21f270de1d8c600ca388" +dependencies = [ + "bumpalo", + "hashbrown 0.14.5", + "ptr_meta", + "rustc-hash 1.1.0", + "triomphe", +] + +[[package]] +name = "swc_atoms" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6567e4e67485b3e7662b486f1565bdae54bd5b9d6b16b2ba1a9babb1e42125" +dependencies = [ + "hstr", + "once_cell", + "rustc-hash 1.1.0", + "serde", +] + +[[package]] +name = "swc_cached" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c" +dependencies = [ + "ahash 0.8.11", + "anyhow", + "dashmap", + "once_cell", + "regex", + "serde", +] + +[[package]] +name = "swc_common" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12d0a8eaaf1606c9207077d75828008cb2dfb51b095a766bd2b72ef893576e31" +dependencies = [ + "ast_node", + "better_scoped_tls", + "cfg-if", + "either", + "from_variant", + "new_debug_unreachable", + "num-bigint", + "once_cell", + "rustc-hash 1.1.0", + "serde", + "siphasher 0.3.11", + "sourcemap 9.1.2", + "swc_allocator", + "swc_atoms", + "swc_eq_ignore_macros", + "swc_visit", + "tracing", + "unicode-width", + "url", +] + +[[package]] +name = "swc_config" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" +dependencies = [ + "anyhow", + "indexmap 2.7.0", + "serde", + "serde_json", + "swc_cached", + "swc_config_macro", +] + +[[package]] +name = "swc_config_macro" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c5f56139042c1a95b54f5ca48baa0e0172d369bcc9d3d473dad1de36bae8399" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "swc_ecma_ast" +version = "0.118.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6f866d12e4d519052b92a0a86d1ac7ff17570da1272ca0c89b3d6f802cd79df" +dependencies = [ + "bitflags 2.6.0", + "is-macro", + "num-bigint", + "phf", + "scoped-tls", + "serde", + "string_enum", + "swc_atoms", + "swc_common", + "unicode-id-start", +] + +[[package]] +name = "swc_ecma_codegen" +version = "0.155.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7641608ef117cfbef9581a99d02059b522fcca75e5244fa0cbbd8606689c6f" +dependencies = [ + "memchr", + "num-bigint", + "once_cell", + "serde", + "sourcemap 9.1.2", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_codegen_macros", + "tracing", +] + +[[package]] +name = "swc_ecma_codegen_macros" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859fabde36db38634f3fad548dd5e3410c1aebba1b67a3c63e67018fa57a0bca" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "swc_ecma_loader" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55fa3d55045b97894bfb04d38aff6d6302ac8a6a38e3bb3dfb0d20475c4974a9" +dependencies = [ + "anyhow", + "pathdiff", + "serde", + "swc_atoms", + "swc_common", + "tracing", +] + +[[package]] +name = "swc_ecma_parser" +version = "0.149.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683dada14722714588b56481399c699378b35b2ba4deb5c4db2fb627a97fb54b" +dependencies = [ + "either", + "new_debug_unreachable", + "num-bigint", + "num-traits", + "phf", + "serde", + "smallvec", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "tracing", + "typed-arena", +] + +[[package]] +name = "swc_ecma_transforms_base" +version = "0.145.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65f21494e75d0bd8ef42010b47cabab9caaed8f2207570e809f6f4eb51a710d1" +dependencies = [ + "better_scoped_tls", + "bitflags 2.6.0", + "indexmap 2.7.0", + "once_cell", + "phf", + "rustc-hash 1.1.0", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_utils", + "swc_ecma_visit", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_classes" +version = "0.134.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3d884594385bea9405a2e1721151470d9a14d3ceec5dd773c0ca6894791601" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_macros" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500a1dadad1e0e41e417d633b3d6d5de677c9e0d3159b94ba3348436cdb15aab" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + +[[package]] +name = "swc_ecma_transforms_proposal" +version = "0.179.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79938ff510fc647febd8c6c3ef4143d099fdad87a223680e632623d056dae2dd" +dependencies = [ + "either", + "rustc-hash 1.1.0", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_transforms_classes", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_react" +version = "0.191.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" +dependencies = [ + "base64 0.21.7", + "dashmap", + "indexmap 2.7.0", + "once_cell", + "serde", + "sha1", + "string_enum", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_transforms_base", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_typescript" +version = "0.198.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15455da4768f97186c40523e83600495210c11825d3a44db43383fd81eace88d" +dependencies = [ + "ryu-js", + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_transforms_react", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_utils" +version = "0.134.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029eec7dd485923a75b5a45befd04510288870250270292fc2c1b3a9e7547408" +dependencies = [ + "indexmap 2.7.0", + "num_cpus", + "once_cell", + "rustc-hash 1.1.0", + "ryu-js", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_visit", + "tracing", + "unicode-id", +] + +[[package]] +name = "swc_ecma_visit" +version = "0.104.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b1c6802e68e51f336e8bc9644e9ff9da75d7da9c1a6247d532f2e908aa33e81" +dependencies = [ + "new_debug_unreachable", + "num-bigint", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_visit", + "tracing", +] + +[[package]] +name = "swc_eq_ignore_macros" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "swc_macros_common" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "swc_visit" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ceb044142ba2719ef9eb3b6b454fce61ab849eb696c34d190f04651955c613d" +dependencies = [ + "either", + "new_debug_unreachable", +] + +[[package]] +name = "swc_visit_macros" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92807d840959f39c60ce8a774a3f83e8193c658068e6d270dbe0a05e40e90b41" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.101", +] + [[package]] name = "syn" version = "1.0.109" @@ -6230,6 +10462,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", + "quote", "unicode-ident", ] @@ -6250,6 +10483,18 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + [[package]] name = "synstructure" version = "0.13.1" @@ -6291,7 +10536,7 @@ checksum = "f8d0582f186c0a6d55655d24543f15e43607299425c5ad8352c242b914b31856" dependencies = [ "aho-corasick", "arc-swap", - "base64", + "base64 0.22.1", "bitpacking", "byteorder", "census", @@ -6308,7 +10553,7 @@ dependencies = [ "lru", "lz4_flex", "measure_time", - "memmap2", + "memmap2 0.9.5", "num_cpus", "once_cell", "oneshot", @@ -6389,7 +10634,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "847434d4af57b32e309f4ab1b4f1707a6c566656264caa427ff4285c4d9d0b82" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -6424,6 +10669,12 @@ dependencies = [ "serde", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "target-lexicon" version = "0.12.16" @@ -6452,6 +10703,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "text_lines" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd5828de7deaa782e1dd713006ae96b3bee32d3279b79eb67ecf8072c059bcf" +dependencies = [ + "serde", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -6546,6 +10806,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tiny-skia" version = "0.11.4" @@ -6557,7 +10826,7 @@ dependencies = [ "bytemuck", "cfg-if", "log", - "png", + "png 0.17.16", "tiny-skia-path", ] @@ -6619,13 +10888,25 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio", + "mio 1.0.3", + "parking_lot 0.12.3", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", ] +[[package]] +name = "tokio-eld" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9166030f05d6bc5642bdb8f8c2be31eb3c02cd465d662bcdc2df82d4aa41a584" +dependencies = [ + "hdrhistogram", + "tokio", +] + [[package]] name = "tokio-macros" version = "2.4.0" @@ -6637,6 +10918,40 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "tokio-metrics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eace09241d62c98b7eeb1107d4c5c64ca3bd7da92e8c218c153ab3a78f9be112" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls 0.23.20", + "tokio", +] + +[[package]] +name = "tokio-socks" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" +dependencies = [ + "either", + "futures-util", + "thiserror 1.0.69", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.17" @@ -6656,8 +10971,12 @@ checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", + "futures-util", + "hashbrown 0.14.5", "pin-project-lite", + "slab", "tokio", ] @@ -6726,13 +11045,13 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64", + "base64 0.22.1", "bytes", - "h2", - "http", - "http-body", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.5.2", "hyper-timeout", "hyper-util", "percent-encoding", @@ -6795,6 +11114,26 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +dependencies = [ + "async-compression", + "bitflags 2.6.0", + "bytes", + "futures-core", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -6884,7 +11223,7 @@ dependencies = [ "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", "once_cell", - "png", + "png 0.17.16", "thiserror 1.0.69", "windows-sys 0.59.0", ] @@ -6897,11 +11236,21 @@ checksum = "aac5e8971f245c3389a5a76e648bfc80803ae066a1243a75db0064d7c1129d63" dependencies = [ "fnv", "memchr", - "nom", + "nom 7.1.3", "once_cell", "petgraph", ] +[[package]] +name = "triomphe" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +dependencies = [ + "serde", + "stable_deref_trait", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -6926,12 +11275,35 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "rand", + "static_assertions", +] + +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typed-path" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41713888c5ccfd99979fcd1afd47b71652e331b3d4a0e19d30769e80fec76cce" +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + [[package]] name = "typenum" version = "1.17.0" @@ -6949,6 +11321,53 @@ dependencies = [ "winapi", ] +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicase" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -6967,6 +11386,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" +[[package]] +name = "unicode-id" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" + +[[package]] +name = "unicode-id-start" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" + [[package]] name = "unicode-ident" version = "1.0.14" @@ -7030,6 +11461,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -7042,11 +11483,11 @@ version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" dependencies = [ - "base64", + "base64 0.22.1", "flate2", "log", "once_cell", - "rustls", + "rustls 0.23.20", "rustls-pki-types", "url", "webpki-roots", @@ -7061,6 +11502,19 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", ] [[package]] @@ -7069,11 +11523,11 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" dependencies = [ - "base64", + "base64 0.22.1", "data-url", "flate2", "fontdb 0.18.0", - "imagesize", + "imagesize 0.12.0", "kurbo 0.11.1", "log", "pico-args", @@ -7090,6 +11544,34 @@ dependencies = [ "xmlwriter", ] +[[package]] +name = "usvg" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447e703d7223b067607655e625e0dbca80822880248937da65966194c4864e6" +dependencies = [ + "base64 0.22.1", + "data-url", + "flate2", + "imagesize 0.13.0", + "kurbo 0.11.1", + "log", + "pico-args", + "roxmltree", + "simplecss", + "siphasher 1.0.1", + "strict-num", + "svgtypes", + "tiny-skia-path", + "xmlwriter", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf16_iter" version = "1.0.5" @@ -7124,6 +11606,38 @@ dependencies = [ "serde", ] +[[package]] +name = "v8" +version = "130.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee0be58935708fa4d7efb970c6cf9f2d9511d24ee24246481a65b6ee167348d" +dependencies = [ + "bindgen", + "bitflags 2.6.0", + "fslock", + "gzip-header", + "home", + "miniz_oxide 0.7.4", + "once_cell", + "paste", + "which 6.0.3", +] + +[[package]] +name = "v8_valueserializer" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97599c400fc79925922b58303e98fcb8fa88f573379a08ddb652e72cbd2e70f6" +dependencies = [ + "bitflags 2.6.0", + "encoding_rs", + "indexmap 2.7.0", + "num-bigint", + "serde", + "thiserror 1.0.69", + "wtf8", +] + [[package]] name = "v_frame" version = "0.3.8" @@ -7141,6 +11655,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "value-trait" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9170e001f458781e92711d2ad666110f153e4e50bfd5cbd02db6547625714187" +dependencies = [ + "float-cmp 0.10.0", + "halfbrown", + "itoa", + "ryu", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -7217,6 +11743,12 @@ version = "0.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "walkdir" version = "2.5.0" @@ -7330,6 +11862,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm_dep_analyzer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f270206a91783fd90625c8bb0d8fbd459d0b1d1bf209b656f713f01ae7c04b8" +dependencies = [ + "thiserror 1.0.69", +] + [[package]] name = "waycrate_xkbkeycode" version = "0.13.99" @@ -7337,7 +11878,7 @@ source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch= dependencies = [ "bitflags 2.6.0", "calloop 0.14.2", - "memmap2", + "memmap2 0.9.5", "smol_str", "tracing", "wayland-backend", @@ -7415,6 +11956,7 @@ dependencies = [ "wayland-backend", "wayland-client", "wayland-scanner", + "wayland-server", ] [[package]] @@ -7467,6 +12009,7 @@ dependencies = [ "wayland-client", "wayland-protocols 0.32.5", "wayland-scanner", + "wayland-server", ] [[package]] @@ -7476,10 +12019,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.36.2", "quote", ] +[[package]] +name = "wayland-server" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89532cc712a2adb119eb4d09694b402576052254d0bb284f82ac1c47fb786ad" +dependencies = [ + "bitflags 2.6.0", + "downcast-rs", + "io-lifetimes", + "rustix", + "wayland-backend", + "wayland-scanner", +] + [[package]] name = "wayland-sys" version = "0.31.5" @@ -7512,6 +12069,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd5da49bdf1f30054cfe0b8ce2958b8fbeb67c4d82c8967a598af481bef255c" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "0.26.7" @@ -7538,7 +12104,7 @@ dependencies = [ "document-features", "js-sys", "log", - "naga", + "naga 23.1.0", "parking_lot 0.12.3", "profiling", "raw-window-handle", @@ -7547,9 +12113,38 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "wgpu-core", - "wgpu-hal", - "wgpu-types", + "wgpu-core 23.0.1", + "wgpu-hal 23.0.1", + "wgpu-types 23.0.0", +] + +[[package]] +name = "wgpu-core" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39" +dependencies = [ + "arrayvec", + "bit-vec 0.6.3", + "bitflags 2.6.0", + "cfg_aliases 0.1.1", + "codespan-reporting", + "document-features", + "indexmap 2.7.0", + "log", + "naga 0.20.0", + "once_cell", + "parking_lot 0.12.3", + "profiling", + "raw-window-handle", + "ron", + "rustc-hash 1.1.0", + "serde", + "smallvec", + "thiserror 1.0.69", + "web-sys", + "wgpu-hal 0.21.1", + "wgpu-types 0.20.0", ] [[package]] @@ -7559,13 +12154,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a" dependencies = [ "arrayvec", - "bit-vec", + "bit-vec 0.8.0", "bitflags 2.6.0", "cfg_aliases 0.1.1", "document-features", "indexmap 2.7.0", "log", - "naga", + "naga 23.1.0", "once_cell", "parking_lot 0.12.3", "profiling", @@ -7573,8 +12168,50 @@ dependencies = [ "rustc-hash 1.1.0", "smallvec", "thiserror 1.0.69", - "wgpu-hal", - "wgpu-types", + "wgpu-hal 23.0.1", + "wgpu-types 23.0.0", +] + +[[package]] +name = "wgpu-hal" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash 0.37.3+1.3.251", + "bit-set 0.5.3", + "bitflags 2.6.0", + "block", + "cfg_aliases 0.1.1", + "core-graphics-types 0.1.3", + "d3d12", + "glow 0.13.1", + "glutin_wgl_sys 0.5.0", + "gpu-alloc", + "gpu-descriptor", + "js-sys", + "khronos-egl", + "libc", + "libloading 0.8.6", + "log", + "metal 0.28.0", + "naga 0.20.0", + "ndk-sys 0.5.0+25.2.9519653", + "objc", + "once_cell", + "parking_lot 0.12.3", + "profiling", + "range-alloc", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "wasm-bindgen", + "web-sys", + "wgpu-types 0.20.0", + "winapi", ] [[package]] @@ -7585,15 +12222,15 @@ checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821" dependencies = [ "android_system_properties", "arrayvec", - "ash", - "bit-set", + "ash 0.38.0+1.3.281", + "bit-set 0.8.0", "bitflags 2.6.0", "block", "bytemuck", "cfg_aliases 0.1.1", "core-graphics-types 0.1.3", - "glow", - "glutin_wgl_sys", + "glow 0.14.2", + "glutin_wgl_sys 0.6.0", "gpu-alloc", "gpu-allocator", "gpu-descriptor", @@ -7602,8 +12239,8 @@ dependencies = [ "libc", "libloading 0.8.6", "log", - "metal", - "naga", + "metal 0.29.0", + "naga 23.1.0", "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", @@ -7617,11 +12254,23 @@ dependencies = [ "thiserror 1.0.69", "wasm-bindgen", "web-sys", - "wgpu-types", + "wgpu-types 23.0.0", "windows", "windows-core 0.58.0", ] +[[package]] +name = "wgpu-types" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" +dependencies = [ + "bitflags 2.6.0", + "js-sys", + "serde", + "web-sys", +] + [[package]] name = "wgpu-types" version = "23.0.0" @@ -7633,6 +12282,42 @@ dependencies = [ "web-sys", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "which" +version = "6.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" +dependencies = [ + "either", + "home", + "rustix", + "winsafe", +] + +[[package]] +name = "which" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a9e33648339dc1642b0e36e21b3385e6148e289226f657c809dee59df5028" +dependencies = [ + "either", + "env_home", + "rustix", + "winsafe", +] + [[package]] name = "whoami" version = "1.5.2" @@ -7641,6 +12326,7 @@ checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ "redox_syscall 0.5.8", "wasite", + "web-sys", ] [[package]] @@ -8001,7 +12687,7 @@ dependencies = [ "dpi", "js-sys", "libc", - "memmap2", + "memmap2 0.9.5", "ndk", "objc2 0.5.2", "objc2-app-kit 0.2.2", @@ -8059,6 +12745,22 @@ dependencies = [ "winapi", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "wl-clipboard-rs" version = "0.8.1" @@ -8091,6 +12793,21 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "wtf8" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01ae8492c38f52376efd3a17d0994b6bcf3df1e39c0226d458b7d81670b2a06" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -8123,12 +12840,47 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom 7.1.3", + "oid-registry", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + [[package]] name = "xcursor" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" + [[package]] name = "xdg-home" version = "1.3.0" @@ -8139,6 +12891,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "xkbcommon" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +dependencies = [ + "libc", + "memmap2 0.8.0", + "xkeysym", +] + [[package]] name = "xkbcommon-dl" version = "0.4.2" @@ -8157,6 +12920,9 @@ name = "xkeysym" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" +dependencies = [ + "bytemuck", +] [[package]] name = "xml-rs" @@ -8197,7 +12963,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.101", - "synstructure", + "synstructure 0.13.1", ] [[package]] @@ -8307,7 +13073,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.101", - "synstructure", + "synstructure 0.13.1", ] [[package]] @@ -8315,6 +13081,20 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] [[package]] name = "zerovec" diff --git a/Cargo.toml b/Cargo.toml index 77705c6..e915712 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ members = [ ] [workspace.package] -edition = "2024" +edition = "2021" [workspace.dependencies] # iced @@ -39,6 +39,7 @@ iced_layershell = { git = "https://github.com/project-gauntlet/exwlshelleventlo gauntlet-common = { path = "./rust/common" } gauntlet-common-ui = { path = "./rust/common_ui" } gauntlet-common-plugin-runtime = { path = "./rust/common_plugin_runtime" } +gauntlet-plugin-runtime = { path = "./rust/plugin_runtime" } gauntlet-management-client = { path = "./rust/management_client" } gauntlet-client = { path = "./rust/client" } gauntlet-server = { path = "./rust/server" } @@ -85,3 +86,6 @@ opt-level = "s" lto = "thin" strip = true +[patch.crates-io] +# NOTE https://github.com/ipetkov/crane/issues/336 +libffi-sys = { git = "https://github.com/tov/libffi-rs", rev = "d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b" } diff --git a/js/build/src/main.ts b/js/build/src/main.ts index 40e2a0b..28d0a37 100644 --- a/js/build/src/main.ts +++ b/js/build/src/main.ts @@ -205,13 +205,7 @@ function build(projectRoot: string, arch: string, profile: string) { } function buildRust(projectRoot: string, arch: string, profile: string) { - console.log("Building rust plugin_runtime...") - const pluginRuntimeRoot = path.resolve(projectRoot, 'rust', 'plugin_runtime'); - spawnWithErrors('cargo', ['build', '--profile', profile, '--features', 'release', '--target', arch], { - cwd: pluginRuntimeRoot - }); - - console.log("Building rust core...") + console.log("Building rust...") spawnWithErrors('cargo', ['build', '--profile', profile, '--features', 'release', '--target', arch], { cwd: projectRoot }); diff --git a/js/scenario_runner_cli/src/main.ts b/js/scenario_runner_cli/src/main.ts index 2390f56..31cbfa1 100644 --- a/js/scenario_runner_cli/src/main.ts +++ b/js/scenario_runner_cli/src/main.ts @@ -35,12 +35,6 @@ async function runScenarios(expectedPlugin: string | undefined) { const scenariosData = path.join(scenarios, "scenarios"); const scenariosRun = path.join(scenarios, "run"); - console.log("Building plugin_runtime") - const pluginRuntimeRoot = path.resolve(projectRoot, 'rust', 'plugin_runtime'); - spawnSync('cargo', ['build', '--features', 'release'], { - cwd: pluginRuntimeRoot - }); - console.log("Building scenario plugins") buildScenarioPlugins(projectRoot) diff --git a/rust/plugin_runtime/Cargo.lock b/rust/plugin_runtime/Cargo.lock deleted file mode 100644 index 1fb556f..0000000 --- a/rust/plugin_runtime/Cargo.lock +++ /dev/null @@ -1,9259 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aead-gcm-stream" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4947a169074c7e038fa43051d1c4e073f4488b0e4b0a30658f1e1a1b06449ce8" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aes-kw" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" -dependencies = [ - "aes", -] - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "aligned-vec" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" -dependencies = [ - "backtrace", -] - -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" - -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -dependencies = [ - "serde", -] - -[[package]] -name = "ash" -version = "0.37.3+1.3.251" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" -dependencies = [ - "libloading 0.7.4", -] - -[[package]] -name = "asn1-rs" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom 7.1.3", - "num-traits", - "rusticata-macros", - "thiserror 1.0.69", - "time", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure 0.12.6", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ast_node" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9184f2b369b3e8625712493c89b785881f27eedc6cde480a81883cef78868b2" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "async-compression" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" -dependencies = [ - "brotli 7.0.0", - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[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.101", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "attohttpc" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "184f5e6cce583a9db6b6f8d772a42cfce5b78e7c3ef26118cec3ce4c8c14969b" -dependencies = [ - "http 1.2.0", - "log", - "rustls 0.22.4", - "url", - "webpki-roots", -] - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "av1-grain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom 7.1.3", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "axum" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" -dependencies = [ - "async-trait", - "axum-core", - "bytes", - "futures-util", - "http 1.2.0", - "http-body 1.0.1", - "http-body-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", - "tower 0.5.2", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 1.2.0", - "http-body 1.0.1", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", -] - -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide 0.8.2", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base32" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64-simd" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" -dependencies = [ - "simd-abstraction", -] - -[[package]] -name = "base64-simd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" -dependencies = [ - "outref 0.5.1", - "vsimd", -] - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "basic-toml" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" -dependencies = [ - "serde", -] - -[[package]] -name = "better_scoped_tls" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297b153aa5e573b5863108a6ddc9d5c968bd0b20e75cc614ee9821d2f45679c7" -dependencies = [ - "scoped-tls", -] - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bincode" -version = "2.0.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95" -dependencies = [ - "bincode_derive", - "serde", -] - -[[package]] -name = "bincode_derive" -version = "2.0.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e30759b3b99a1b802a7a3aa21c85c3ded5c28e1c83170d82d70f08bbf7f3e4c" -dependencies = [ - "virtue", -] - -[[package]] -name = "bindgen" -version = "0.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" -dependencies = [ - "bitflags 2.6.0", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.101", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -dependencies = [ - "serde", -] - -[[package]] -name = "bitstream-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" -dependencies = [ - "objc2", -] - -[[package]] -name = "boxed_error" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" -dependencies = [ - "quote", - "syn 2.0.101", -] - -[[package]] -name = "brotli" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bstr" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "built" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b" - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -dependencies = [ - "allocator-api2", -] - -[[package]] -name = "bytemuck" -version = "1.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - -[[package]] -name = "bytes" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" - -[[package]] -name = "cacao" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5952f0958672e4aa8fc706d01905c56af57759e078c53a6fddf4a13361943e7a" -dependencies = [ - "block", - "core-foundation", - "core-graphics", - "dispatch", - "lazy_static", - "libc", - "objc", - "objc_id", - "os_info", - "url", -] - -[[package]] -name = "cache_control" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2a5fb3207c12b5d208ebc145f967fea5cac41a021c37417ccc31ba40f39ee" - -[[package]] -name = "calloop" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" -dependencies = [ - "bitflags 2.6.0", - "log", - "polling", - "rustix", - "slab", - "thiserror 1.0.69", -] - -[[package]] -name = "calloop-wayland-source" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" -dependencies = [ - "calloop", - "rustix", - "wayland-backend", - "wayland-client", -] - -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - -[[package]] -name = "cc" -version = "1.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" -dependencies = [ - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom 7.1.3", -] - -[[package]] -name = "cfg-expr" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" -dependencies = [ - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "chrono" -version = "0.4.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "serde", - "windows-targets 0.52.6", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading 0.8.6", -] - -[[package]] -name = "clipboard-win" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" -dependencies = [ - "error-code", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "color-print" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aa954171903797d5623e047d9ab69d91b493657917bdfb8c2c80ecaf9cdb6f4" -dependencies = [ - "color-print-proc-macro", -] - -[[package]] -name = "color-print-proc-macro" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" -dependencies = [ - "nom 7.1.3", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[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 = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", -] - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "convert_case" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "cooked-waker" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types 0.3.2", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] - -[[package]] -name = "cosmic-protocols" -version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-protocols.git#d218c76b58c7a3b20dd5e7943f93fc306a1b81b8" -dependencies = [ - "bitflags 2.6.0", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-protocols-wlr", - "wayland-scanner", - "wayland-server", -] - -[[package]] -name = "cpufeatures" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "rand_core", - "typenum", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "cursor-icon" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto 0.2.9", - "rustc_version 0.4.1", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "d3d12" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" -dependencies = [ - "bitflags 2.6.0", - "libloading 0.8.6", - "winapi", -] - -[[package]] -name = "darling" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.101", -] - -[[package]] -name = "darling_macro" -version = "0.20.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "data-encoding" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - -[[package]] -name = "data-url" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b319d1b62ffbd002e057f36bebd1f42b9f97927c9577461d855f3513c4289f" - -[[package]] -name = "debugid" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" -dependencies = [ - "serde", - "uuid", -] - -[[package]] -name = "deflate" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" -dependencies = [ - "adler32", - "byteorder", -] - -[[package]] -name = "deno_ast" -version = "0.43.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d00b724e06d2081a141ec1155756a0b465d413d8e2a7515221f61d482eb2ee" -dependencies = [ - "base64 0.21.7", - "deno_media_type", - "deno_terminal 0.1.1", - "dprint-swc-ext", - "once_cell", - "percent-encoding", - "serde", - "sourcemap 9.1.2", - "swc_atoms", - "swc_common", - "swc_config", - "swc_config_macro", - "swc_ecma_ast", - "swc_ecma_codegen", - "swc_ecma_codegen_macros", - "swc_ecma_loader", - "swc_ecma_parser", - "swc_ecma_transforms_base", - "swc_ecma_transforms_classes", - "swc_ecma_transforms_macros", - "swc_ecma_transforms_proposal", - "swc_ecma_transforms_react", - "swc_ecma_transforms_typescript", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_eq_ignore_macros", - "swc_macros_common", - "swc_visit", - "swc_visit_macros", - "text_lines", - "thiserror 1.0.69", - "unicode-width", - "url", -] - -[[package]] -name = "deno_broadcast_channel" -version = "0.173.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348ecdacfdd262e6b2f9740d07a41e8f4d79d06a670378a060515d0208495c9f" -dependencies = [ - "async-trait", - "deno_core", - "thiserror 1.0.69", - "tokio", - "uuid", -] - -[[package]] -name = "deno_cache" -version = "0.111.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6e35cb122e56c22149652327c90c563790ddcef24ea1fc77454e193131318e" -dependencies = [ - "async-trait", - "deno_core", - "rusqlite", - "serde", - "sha2", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "deno_canvas" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bbfd1437bc01ab775b1a60e3061bbf2e9517e31fb5eedf89b2b703104c835e6" -dependencies = [ - "deno_core", - "deno_webgpu", - "image 0.24.9", - "serde", - "thiserror 1.0.69", -] - -[[package]] -name = "deno_console" -version = "0.179.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e09f2bbb2d842329b602da25dbab5cd4a342f9a8adcb7c02509fc322f796e79" -dependencies = [ - "deno_core", -] - -[[package]] -name = "deno_core" -version = "0.321.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd2a54cda74cdc187d5fc2d23370a45cf09f912caf566dd1cd24a50157d809c7" -dependencies = [ - "anyhow", - "bincode 1.3.3", - "bit-set", - "bit-vec", - "bytes", - "cooked-waker", - "deno_core_icudata", - "deno_ops", - "deno_unsync", - "futures", - "indexmap 2.7.0", - "libc", - "memoffset", - "parking_lot", - "percent-encoding", - "pin-project", - "serde", - "serde_json", - "serde_v8", - "smallvec", - "sourcemap 8.0.1", - "static_assertions", - "tokio", - "url", - "v8", - "wasm_dep_analyzer", -] - -[[package]] -name = "deno_core_icudata" -version = "0.74.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" - -[[package]] -name = "deno_cron" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f936f036e9e3f88205db8efd0ec68c65efb47bc0cbe4b715bafecd6e9c407931" -dependencies = [ - "anyhow", - "async-trait", - "chrono", - "deno_core", - "saffron", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "deno_crypto" -version = "0.193.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b582f30887c7c0902b4445c64d7c8b98d0043ec547c44de8de26104b093e1be" -dependencies = [ - "aes", - "aes-gcm", - "aes-kw", - "base64 0.21.7", - "cbc", - "const-oid", - "ctr", - "curve25519-dalek", - "deno_core", - "deno_web", - "ed448-goldilocks", - "elliptic-curve", - "num-traits", - "once_cell", - "p256", - "p384", - "p521", - "rand", - "ring", - "rsa", - "sec1", - "serde", - "serde_bytes", - "sha1", - "sha2", - "signature", - "spki", - "thiserror 1.0.69", - "tokio", - "uuid", - "x25519-dalek", -] - -[[package]] -name = "deno_fetch" -version = "0.203.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18e66bd3bf786e24a8b8bdc97049fa82957b095a5fd1e142545c5a7cdd2272a" -dependencies = [ - "base64 0.21.7", - "bytes", - "data-url", - "deno_core", - "deno_permissions", - "deno_tls", - "dyn-clone", - "error_reporter", - "hickory-resolver", - "http 1.2.0", - "http-body-util", - "hyper 1.5.2", - "hyper-rustls", - "hyper-util", - "ipnet", - "percent-encoding", - "rustls-webpki", - "serde", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tokio-rustls", - "tokio-socks", - "tokio-util", - "tower 0.4.13", - "tower-http", - "tower-service", -] - -[[package]] -name = "deno_ffi" -version = "0.166.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6d2f13ebfa93833446abeb3bd1836fdf86bcb96678276b21a0622146f42284" -dependencies = [ - "deno_core", - "deno_permissions", - "dlopen2 0.6.1", - "dynasmrt", - "libffi", - "libffi-sys", - "log", - "num-bigint", - "serde", - "serde-value", - "serde_json", - "thiserror 1.0.69", - "tokio", - "winapi", -] - -[[package]] -name = "deno_fs" -version = "0.89.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f53829328c344736d7fdda44733057299536f3379513cdcd258823ef273540ec" -dependencies = [ - "async-trait", - "base32", - "boxed_error", - "deno_core", - "deno_io", - "deno_path_util", - "deno_permissions", - "filetime", - "junction", - "libc", - "nix", - "rand", - "rayon", - "serde", - "thiserror 1.0.69", - "winapi", - "windows-sys 0.52.0", -] - -[[package]] -name = "deno_http" -version = "0.177.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42b4ee6dbac20aa287a416f8905ed64b95cb484063c2af6be4eb232382c7fcb6" -dependencies = [ - "async-compression", - "async-trait", - "base64 0.21.7", - "brotli 6.0.0", - "bytes", - "cache_control", - "deno_core", - "deno_net", - "deno_websocket", - "flate2", - "http 0.2.12", - "http 1.2.0", - "httparse", - "hyper 0.14.32", - "hyper 1.5.2", - "hyper-util", - "itertools 0.10.5", - "memmem", - "mime", - "once_cell", - "percent-encoding", - "phf", - "pin-project", - "ring", - "scopeguard", - "serde", - "smallvec", - "thiserror 1.0.69", - "tokio", - "tokio-util", -] - -[[package]] -name = "deno_io" -version = "0.89.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc19195805a6b256d5ffe697c81ac79f8acd22246616fe880d6c9ec2dacf9bb4" -dependencies = [ - "async-trait", - "deno_core", - "filetime", - "fs3", - "libc", - "log", - "once_cell", - "os_pipe", - "parking_lot", - "pin-project", - "rand", - "tokio", - "uuid", - "winapi", - "windows-sys 0.52.0", -] - -[[package]] -name = "deno_kv" -version = "0.87.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a25347cd7ae561d0b05c24eebb3047e85a3af3f398675d5a9894fd167f2714f" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.21.7", - "boxed_error", - "bytes", - "chrono", - "deno_core", - "deno_fetch", - "deno_path_util", - "deno_permissions", - "deno_tls", - "denokv_proto", - "denokv_remote", - "denokv_sqlite", - "faster-hex", - "http 1.2.0", - "http-body-util", - "log", - "num-bigint", - "prost", - "prost-build", - "rand", - "rusqlite", - "serde", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "deno_media_type" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa135b8a9febc9a51c16258e294e268a1276750780d69e46edb31cced2826e4" -dependencies = [ - "data-url", - "serde", - "url", -] - -[[package]] -name = "deno_napi" -version = "0.110.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea57b67488969f82594cb008fed1bd99830e6db042e31ee9878933d8c76be41c" -dependencies = [ - "deno_core", - "deno_permissions", - "libc", - "libloading 0.7.4", - "log", - "napi_sym", - "thiserror 1.0.69", - "windows-sys 0.52.0", -] - -[[package]] -name = "deno_native_certs" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86bc737e098a45aa5742d51ce694ac7236a1e69fb0d9df8c862e9b4c9583c5f9" -dependencies = [ - "dlopen2 0.7.0", - "dlopen2_derive", - "once_cell", - "rustls-native-certs", - "rustls-pemfile", -] - -[[package]] -name = "deno_net" -version = "0.171.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b3a51f7b4d5d64d17a7bc6f7495498f20d809930979d21a059d75e850cdea6" -dependencies = [ - "deno_core", - "deno_permissions", - "deno_tls", - "hickory-proto", - "hickory-resolver", - "pin-project", - "rustls-tokio-stream", - "serde", - "socket2", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "deno_node" -version = "0.116.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd0d1a757f75224e84ce8a553c2465e4a352fba4b7551ec15809d8a119847e7" -dependencies = [ - "aead-gcm-stream", - "aes", - "async-trait", - "base64 0.21.7", - "blake2", - "boxed_error", - "brotli 6.0.0", - "bytes", - "cbc", - "const-oid", - "data-encoding", - "deno_core", - "deno_fetch", - "deno_fs", - "deno_io", - "deno_media_type", - "deno_net", - "deno_package_json", - "deno_path_util", - "deno_permissions", - "deno_whoami", - "der", - "digest", - "dsa", - "ecb", - "ecdsa", - "ed25519-dalek", - "elliptic-curve", - "errno 0.2.8", - "faster-hex", - "h2 0.4.7", - "hkdf", - "home", - "http 1.2.0", - "http-body-util", - "hyper 1.5.2", - "hyper-util", - "idna", - "indexmap 2.7.0", - "ipnetwork", - "k256", - "lazy-regex", - "libc", - "libz-sys", - "md-5", - "md4", - "memchr", - "node_resolver", - "num-bigint", - "num-bigint-dig", - "num-integer", - "num-traits", - "once_cell", - "p224", - "p256", - "p384", - "path-clean", - "pbkdf2", - "pin-project-lite", - "pkcs8", - "rand", - "regex", - "ring", - "ripemd", - "rsa", - "scrypt", - "sec1", - "serde", - "sha1", - "sha2", - "sha3", - "signature", - "simd-json", - "sm3", - "spki", - "stable_deref_trait", - "thiserror 1.0.69", - "tokio", - "tokio-eld", - "url", - "webpki-root-certs", - "winapi", - "windows-sys 0.52.0", - "x25519-dalek", - "x509-parser", - "yoke", -] - -[[package]] -name = "deno_ops" -version = "0.197.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a8825d92301cf445727c43f17fee2a20fcdf4370004339965156ae7c56c97e" -dependencies = [ - "proc-macro-rules", - "proc-macro2", - "quote", - "stringcase", - "strum", - "strum_macros", - "syn 2.0.101", - "thiserror 1.0.69", -] - -[[package]] -name = "deno_package_json" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbc4c4d3eb0960b58e8f43f9fc2d3f620fcac9a03cd85203e08db5b04e83c1f" -dependencies = [ - "deno_semver", - "indexmap 2.7.0", - "serde", - "serde_json", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "deno_path_util" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff25f6e08e7a0214bbacdd6f7195c7f1ebcd850c87a624e4ff06326b68b42d99" -dependencies = [ - "percent-encoding", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "deno_permissions" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e822f98185ab3ddf06104b2407681e0008af52361af32f1cd171b7eda5aa59" -dependencies = [ - "deno_core", - "deno_path_util", - "deno_terminal 0.2.0", - "fqdn", - "libc", - "log", - "once_cell", - "percent-encoding", - "serde", - "thiserror 1.0.69", - "which 4.4.2", - "winapi", -] - -[[package]] -name = "deno_runtime" -version = "0.188.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516ed4f796ab0f5dc092b5592ed6159c759f4f3a94f4a23455fecc94edc51dd1" -dependencies = [ - "async-trait", - "color-print", - "deno_ast", - "deno_broadcast_channel", - "deno_cache", - "deno_canvas", - "deno_console", - "deno_core", - "deno_cron", - "deno_crypto", - "deno_fetch", - "deno_ffi", - "deno_fs", - "deno_http", - "deno_io", - "deno_kv", - "deno_napi", - "deno_net", - "deno_node", - "deno_path_util", - "deno_permissions", - "deno_terminal 0.2.0", - "deno_tls", - "deno_url", - "deno_web", - "deno_webgpu", - "deno_webidl", - "deno_websocket", - "deno_webstorage", - "dlopen2 0.6.1", - "encoding_rs", - "fastwebsockets", - "flate2", - "http 1.2.0", - "http-body-util", - "hyper 0.14.32", - "hyper 1.5.2", - "hyper-util", - "libc", - "log", - "netif", - "nix", - "node_resolver", - "notify", - "ntapi", - "once_cell", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-otlp", - "opentelemetry-semantic-conventions", - "opentelemetry_sdk", - "percent-encoding", - "pin-project", - "regex", - "rustyline", - "same-file", - "serde", - "signal-hook", - "signal-hook-registry", - "tempfile", - "thiserror 1.0.69", - "tokio", - "tokio-metrics", - "twox-hash", - "uuid", - "which 4.4.2", - "winapi", - "windows-sys 0.52.0", -] - -[[package]] -name = "deno_semver" -version = "0.5.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c957c6a57c38b7dde2315df0da0ec228911e56a74f185b108a488d0401841a67" -dependencies = [ - "monch", - "once_cell", - "serde", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "deno_terminal" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6337d4e7f375f8b986409a76fbeecfa4bd8a1343e63355729ae4befa058eaf" -dependencies = [ - "once_cell", - "termcolor", -] - -[[package]] -name = "deno_terminal" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daef12499e89ee99e51ad6000a91f600d3937fb028ad4918af76810c5bc9e0d5" -dependencies = [ - "once_cell", - "termcolor", -] - -[[package]] -name = "deno_tls" -version = "0.166.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688175eed35e7b3053ec114227894ef24786855405d8844058a48bffa997d85a" -dependencies = [ - "deno_core", - "deno_native_certs", - "rustls 0.23.20", - "rustls-pemfile", - "rustls-tokio-stream", - "rustls-webpki", - "serde", - "thiserror 1.0.69", - "tokio", - "webpki-roots", -] - -[[package]] -name = "deno_unsync" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d774fd83f26b24f0805a6ab8b26834a0d06ceac0db517b769b1e4633c96a2057" -dependencies = [ - "futures", - "parking_lot", - "tokio", -] - -[[package]] -name = "deno_url" -version = "0.179.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9a108794e505f2b07665e19ff336c1bcba6adcf7182c90c1d3a6c741d7fcd0" -dependencies = [ - "deno_core", - "thiserror 1.0.69", - "urlpattern", -] - -[[package]] -name = "deno_web" -version = "0.210.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7679087bcc41f7ae3385f8c12d43bc81cfc54cb9b1ef73983d20f5e39fa4e0da" -dependencies = [ - "async-trait", - "base64-simd 0.8.0", - "bytes", - "deno_core", - "deno_permissions", - "encoding_rs", - "flate2", - "futures", - "serde", - "thiserror 1.0.69", - "tokio", - "uuid", -] - -[[package]] -name = "deno_webgpu" -version = "0.146.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f78b73638be1552b31778e42267f4fb47e902f7b261bdb0f951ba2b1d6bfab" -dependencies = [ - "deno_core", - "raw-window-handle", - "serde", - "thiserror 1.0.69", - "tokio", - "wgpu-core", - "wgpu-types", -] - -[[package]] -name = "deno_webidl" -version = "0.179.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b55d845e3d64f8de7eff67aaa4b6fe1b23bbc2efe967c984f8c64c8dd85fad4" -dependencies = [ - "deno_core", -] - -[[package]] -name = "deno_websocket" -version = "0.184.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00407052c6524828f2708557c47059ba9b87874758416c66f47f5102ac68422" -dependencies = [ - "bytes", - "deno_core", - "deno_net", - "deno_permissions", - "deno_tls", - "fastwebsockets", - "h2 0.4.7", - "http 1.2.0", - "http-body-util", - "hyper 1.5.2", - "hyper-util", - "once_cell", - "rustls-tokio-stream", - "serde", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "deno_webstorage" -version = "0.174.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecaabbb1580d21811642f11cc12fe8599684efeb9398eaa998a3db8811e8edc" -dependencies = [ - "deno_core", - "deno_web", - "rusqlite", - "thiserror 1.0.69", -] - -[[package]] -name = "deno_whoami" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e75e4caa92b98a27f09c671d1399aee0f5970aa491b9a598523aac000a2192e3" -dependencies = [ - "libc", - "whoami", -] - -[[package]] -name = "denokv_proto" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ba1f99ed11a9c11e868a8521b1f71a7e1aba785d7f42ea9ecbdc01146c89ec" -dependencies = [ - "anyhow", - "async-trait", - "chrono", - "futures", - "num-bigint", - "prost", - "serde", - "uuid", -] - -[[package]] -name = "denokv_remote" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ed833073189e8f6d03155fe3b05a024e75e29d8a28a4c2e9ec3b5c925e727b" -dependencies = [ - "anyhow", - "async-stream", - "async-trait", - "bytes", - "chrono", - "denokv_proto", - "futures", - "http 1.2.0", - "log", - "prost", - "rand", - "serde", - "serde_json", - "tokio", - "tokio-util", - "url", - "uuid", -] - -[[package]] -name = "denokv_sqlite" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b790f01d1302d53a0c3cbd27de88a06b3abd64ec8ab8673924e490541c7c713" -dependencies = [ - "anyhow", - "async-stream", - "async-trait", - "chrono", - "denokv_proto", - "futures", - "hex", - "log", - "num-bigint", - "rand", - "rusqlite", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tokio-stream", - "uuid", - "v8_valueserializer", -] - -[[package]] -name = "der" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" -dependencies = [ - "const-oid", - "der_derive", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "der-parser" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom 7.1.3", - "num-bigint", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "der_derive" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", - "serde", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "directories" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "dlib" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" -dependencies = [ - "libloading 0.8.6", -] - -[[package]] -name = "dlopen2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bc2c7ed06fd72a8513ded8d0d2f6fd2655a85d6885c48cae8625d80faf28c03" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - -[[package]] -name = "doctest-file" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" - -[[package]] -name = "document-features" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" -dependencies = [ - "litrs", -] - -[[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" - -[[package]] -name = "dprint-swc-ext" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba28c12892aadb751c2ba7001d8460faee4748a04b4edc51c7121cc67ee03db" -dependencies = [ - "num-bigint", - "rustc-hash 1.1.0", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_parser", - "text_lines", -] - -[[package]] -name = "dsa" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" -dependencies = [ - "digest", - "num-bigint-dig", - "num-traits", - "pkcs8", - "rfc6979", - "sha2", - "signature", - "zeroize", -] - -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - -[[package]] -name = "dynasm" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" -dependencies = [ - "bitflags 1.3.2", - "byteorder", - "lazy_static", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "dynasmrt" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" -dependencies = [ - "byteorder", - "dynasm", - "memmap2 0.5.10", -] - -[[package]] -name = "ecb" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" -dependencies = [ - "cipher", -] - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core", - "serde", - "sha2", - "signature", - "subtle", - "zeroize", -] - -[[package]] -name = "ed448-goldilocks" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06924531e9e90130842b012e447f85bdaf9161bc8a0f8092be8cb70b01ebe092" -dependencies = [ - "fiat-crypto 0.1.20", - "hex", - "subtle", - "zeroize", -] - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "base64ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core", - "sec1", - "serde_json", - "serdect", - "subtle", - "zeroize", -] - -[[package]] -name = "encoding" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" -dependencies = [ - "encoding-index-japanese", - "encoding-index-korean", - "encoding-index-simpchinese", - "encoding-index-singlebyte", - "encoding-index-tradchinese", -] - -[[package]] -name = "encoding-index-japanese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-korean" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-simpchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-singlebyte" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-tradchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding_index_tests" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "env_home" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "erased-serde" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" -dependencies = [ - "serde", - "typeid", -] - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "error-code" -version = "3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" - -[[package]] -name = "error_reporter" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ae425815400e5ed474178a7a22e275a9687086a12ca63ec793ff292d8fdae8" - -[[package]] -name = "exr" -version = "1.73.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" -dependencies = [ - "bit_field", - "half", - "lebe", - "miniz_oxide 0.8.2", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - -[[package]] -name = "faster-hex" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" -dependencies = [ - "serde", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "fastwebsockets" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26da0c7b5cef45c521a6f9cdfffdfeb6c9f5804fbac332deb5ae254634c7a6be" -dependencies = [ - "base64 0.21.7", - "bytes", - "http-body-util", - "hyper 1.5.2", - "hyper-util", - "pin-project", - "rand", - "sha1", - "simdutf8", - "thiserror 1.0.69", - "tokio", - "utf-8", -] - -[[package]] -name = "fd-lock" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" -dependencies = [ - "cfg-if", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "filetime" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" -dependencies = [ - "crc32fast", - "miniz_oxide 0.8.2", -] - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - -[[package]] -name = "float-cmp" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" -dependencies = [ - "num-traits", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared 0.3.1", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fqdn" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb540cf7bc4fe6df9d8f7f0c974cfd0dce8ed4e9e8884e73433b503ee78b4e7d" - -[[package]] -name = "freedesktop-icons" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8ef34245e0540c9a3ce7a28340b98d2c12b75da0d446da4e8224923fcaa0c16" -dependencies = [ - "dirs", - "once_cell", - "rust-ini", - "thiserror 1.0.69", - "xdg", -] - -[[package]] -name = "freedesktop_entry_parser" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db9c27b72f19a99a895f8ca89e2d26e4ef31013376e56fdafef697627306c3e4" -dependencies = [ - "nom 7.1.3", - "thiserror 1.0.69", -] - -[[package]] -name = "from_variant" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" -dependencies = [ - "proc-macro2", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "fs3" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb17cf6ed704f72485332f6ab65257460c4f9f3083934cf402bf9f5b3b600a90" -dependencies = [ - "libc", - "rustc_version 0.2.3", - "winapi", -] - -[[package]] -name = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - -[[package]] -name = "fslock" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "gauntlet-common" -version = "0.0.0" -dependencies = [ - "anyhow", - "base64 0.22.1", - "bincode 2.0.0-rc.3", - "bytes", - "convert_case 0.6.0", - "directories", - "gauntlet-component-model", - "gauntlet-utils", - "gauntlet-utils-macros", - "gix-url", - "indexmap 2.7.0", - "itertools 0.13.0", - "libc", - "prost", - "serde", - "serde_json", - "thiserror 2.0.8", - "tokio", - "tonic", - "tonic-build", -] - -[[package]] -name = "gauntlet-common-plugin-runtime" -version = "0.1.0" -dependencies = [ - "anyhow", - "bincode 2.0.0-rc.3", - "gauntlet-common", - "gauntlet-utils", - "gauntlet-utils-macros", - "interprocess", - "once_cell", - "regex", - "serde", - "tokio", - "tracing", -] - -[[package]] -name = "gauntlet-component-model" -version = "0.0.0" -dependencies = [ - "anyhow", - "indexmap 2.7.0", - "serde", - "serde_json", -] - -[[package]] -name = "gauntlet-plugin-runtime" -version = "0.1.0" -dependencies = [ - "anyhow", - "bincode 2.0.0-rc.3", - "bytes", - "cacao", - "cosmic-protocols", - "deno_core", - "deno_runtime", - "encoding", - "freedesktop-icons", - "freedesktop_entry_parser", - "futures", - "gauntlet-common", - "gauntlet-common-plugin-runtime", - "gauntlet-component-model", - "gauntlet-utils", - "icns", - "image 0.25.5", - "indexmap 2.7.0", - "interprocess", - "libc", - "numbat", - "objc2", - "objc2-app-kit", - "objc2-foundation", - "once_cell", - "open", - "plist", - "regex", - "resvg", - "serde", - "smithay-client-toolkit", - "sys-locale", - "tokio", - "tokio-util", - "tracing", - "typed-path", - "uuid", - "walkdir", - "wayland-client", - "wayland-protocols-wlr", - "which 7.0.1", - "windows", - "x11rb", -] - -[[package]] -name = "gauntlet-utils" -version = "0.0.0" -dependencies = [ - "anyhow", - "prost", - "thiserror 2.0.8", - "tokio", - "tonic", -] - -[[package]] -name = "gauntlet-utils-macros" -version = "0.1.0" -dependencies = [ - "convert_case 0.8.0", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "gethostname" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" -dependencies = [ - "libc", - "windows-targets 0.48.5", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gif" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - -[[package]] -name = "gix-features" -version = "0.39.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d85d673f2e022a340dba4713bed77ef2cf4cd737d2f3e0f159d45e0935fd81f" -dependencies = [ - "gix-hash", - "gix-trace", - "libc", -] - -[[package]] -name = "gix-hash" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5eccc17194ed0e67d49285e4853307e4147e95407f91c1c3e4a13ba9f4e4ce" -dependencies = [ - "faster-hex", - "thiserror 2.0.8", -] - -[[package]] -name = "gix-path" -version = "0.10.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc292ef1a51e340aeb0e720800338c805975724c1dfbd243185452efd8645b7" -dependencies = [ - "bstr", - "gix-trace", - "home", - "once_cell", - "thiserror 2.0.8", -] - -[[package]] -name = "gix-trace" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04bdde120c29f1fc23a24d3e115aeeea3d60d8e65bab92cc5f9d90d9302eb952" - -[[package]] -name = "gix-url" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e09f97db3618fb8e473d7d97e77296b50aaee0ddcd6a867f07443e3e87391099" -dependencies = [ - "bstr", - "gix-features", - "gix-path", - "thiserror 2.0.8", - "url", -] - -[[package]] -name = "gl_generator" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" -dependencies = [ - "khronos_api", - "log", - "xml-rs", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "glow" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" -dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "glutin_wgl_sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" -dependencies = [ - "gl_generator", -] - -[[package]] -name = "gpu-alloc" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" -dependencies = [ - "bitflags 2.6.0", - "gpu-alloc-types", -] - -[[package]] -name = "gpu-alloc-types" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "gpu-descriptor" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" -dependencies = [ - "bitflags 2.6.0", - "gpu-descriptor-types", - "hashbrown 0.15.2", -] - -[[package]] -name = "gpu-descriptor-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - -[[package]] -name = "gzip-header" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" -dependencies = [ - "crc32fast", -] - -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.7.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "h2" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http 1.2.0", - "indexmap 2.7.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "half" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" -dependencies = [ - "cfg-if", - "crunchy", -] - -[[package]] -name = "halfbrown" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8588661a8607108a5ca69cab034063441a0413a0b041c13618a7dd348021ef6f" -dependencies = [ - "hashbrown 0.14.5", - "serde", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashlink" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "hdrhistogram" -version = "7.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" -dependencies = [ - "base64 0.21.7", - "byteorder", - "crossbeam-channel", - "flate2", - "nom 7.1.3", - "num-traits", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hexf-parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" - -[[package]] -name = "hickory-proto" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna", - "ipnet", - "once_cell", - "rand", - "serde", - "thiserror 1.0.69", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "hickory-resolver" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" -dependencies = [ - "cfg-if", - "futures-util", - "hickory-proto", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand", - "resolv-conf", - "serde", - "smallvec", - "thiserror 1.0.69", - "tokio", - "tracing", -] - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - -[[package]] -name = "hstr" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412" -dependencies = [ - "hashbrown 0.14.5", - "new_debug_unreachable", - "once_cell", - "phf", - "rustc-hash 1.1.0", - "triomphe", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http 1.2.0", -] - -[[package]] -name = "http-body-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" -dependencies = [ - "bytes", - "futures-util", - "http 1.2.0", - "http-body 1.0.1", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - -[[package]] -name = "hyper" -version = "0.14.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2 0.4.7", - "http 1.2.0", - "http-body 1.0.1", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6884a48c6826ec44f524c7456b163cebe9e55a18d7b5e307cb4f100371cc767" -dependencies = [ - "futures-util", - "http 1.2.0", - "hyper 1.5.2", - "hyper-util", - "rustls 0.23.20", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-timeout" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" -dependencies = [ - "hyper 1.5.2", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http 1.2.0", - "http-body 1.0.1", - "hyper 1.5.2", - "pin-project-lite", - "socket2", - "tokio", - "tower 0.4.13", - "tower-service", - "tracing", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core 0.52.0", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icns" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ccfbad7e08da70a5b48a924994a5afd93125ce5d45a3b0ba0b8da7bda59a40" -dependencies = [ - "byteorder", - "png 0.16.8", -] - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - -[[package]] -name = "image" -version = "0.24.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "num-traits", - "png 0.17.16", -] - -[[package]] -name = "image" -version = "0.25.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" -dependencies = [ - "bytemuck", - "byteorder-lite", - "color_quant", - "exr", - "gif", - "image-webp", - "num-traits", - "png 0.17.16", - "qoi", - "ravif", - "rayon", - "rgb", - "tiff", - "zune-core", - "zune-jpeg", -] - -[[package]] -name = "image-webp" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" -dependencies = [ - "byteorder-lite", - "quick-error 2.0.1", -] - -[[package]] -name = "imagesize" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" - -[[package]] -name = "imgref" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" -dependencies = [ - "equivalent", - "hashbrown 0.15.2", - "serde", -] - -[[package]] -name = "inotify" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" -dependencies = [ - "bitflags 1.3.2", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "interprocess" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "894148491d817cb36b6f778017b8ac46b17408d522dd90f539d677ea938362eb" -dependencies = [ - "doctest-file", - "futures-core", - "libc", - "recvmsg", - "tokio", - "widestring", - "windows-sys 0.52.0", -] - -[[package]] -name = "io-lifetimes" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" - -[[package]] -name = "ipconfig" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" -dependencies = [ - "socket2", - "widestring", - "windows-sys 0.48.0", - "winreg", -] - -[[package]] -name = "ipnet" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" - -[[package]] -name = "ipnetwork" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" -dependencies = [ - "serde", -] - -[[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-macro" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[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 = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" - -[[package]] -name = "jiff" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db69f08d4fb10524cacdb074c10b296299d71274ddbc830a8ee65666867002e9" -dependencies = [ - "jiff-tzdb-platform", - "js-sys", - "wasm-bindgen", - "windows-sys 0.59.0", -] - -[[package]] -name = "jiff-tzdb" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91335e575850c5c4c673b9bd467b0e025f164ca59d0564f69d0c2ee0ffad4653" - -[[package]] -name = "jiff-tzdb-platform" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9835f0060a626fe59f160437bc725491a6af23133ea906500027d1bd2f8f4329" -dependencies = [ - "jiff-tzdb", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" -dependencies = [ - "libc", -] - -[[package]] -name = "jpeg-decoder" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" - -[[package]] -name = "js-sys" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "junction" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be39922b087cecaba4e2d5592dedfc8bda5d4a5a1231f143337cca207950b61d" -dependencies = [ - "scopeguard", - "winapi", -] - -[[package]] -name = "k256" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", - "signature", -] - -[[package]] -name = "keccak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "khronos-egl" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" -dependencies = [ - "libc", - "libloading 0.8.6", - "pkg-config", -] - -[[package]] -name = "khronos_api" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" - -[[package]] -name = "kqueue" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "kurbo" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f" -dependencies = [ - "arrayvec", - "smallvec", -] - -[[package]] -name = "lazy-regex" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d8e41c97e6bc7ecb552016274b99fbb5d035e8de288c582d9b933af6677bfda" -dependencies = [ - "lazy-regex-proc_macros", - "once_cell", - "regex", -] - -[[package]] -name = "lazy-regex-proc_macros" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e1d8b05d672c53cb9c7b920bbba8783845ae4f0b076e02a3db1d02c81b4163" -dependencies = [ - "proc-macro2", - "quote", - "regex", - "syn 2.0.101", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "lebe" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" - -[[package]] -name = "libc" -version = "0.2.169" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" - -[[package]] -name = "libffi" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2" -dependencies = [ - "libc", - "libffi-sys", -] - -[[package]] -name = "libffi-sys" -version = "2.3.0" -source = "git+https://github.com/tov/libffi-rs?rev=d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b#d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b" -dependencies = [ - "cc", -] - -[[package]] -name = "libfuzzer-sys" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" -dependencies = [ - "arbitrary", - "cc", -] - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libloading" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" -dependencies = [ - "cfg-if", - "windows-targets 0.52.6", -] - -[[package]] -name = "libm" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.6.0", - "libc", - "redox_syscall", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "litemap" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" - -[[package]] -name = "litrs" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "loop9" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] - -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - -[[package]] -name = "md4" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da5ac363534dce5fabf69949225e174fbf111a498bf0ff794c8ea1fba9f3dda" -dependencies = [ - "digest", -] - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - -[[package]] -name = "memmap2" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" -dependencies = [ - "libc", -] - -[[package]] -name = "memmap2" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" -dependencies = [ - "libc", -] - -[[package]] -name = "memmem" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" - -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mendeleev" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f8dd6ec5207f7f69db7abb42466511394956dc85faf163de1fe393246c8b7e4" -dependencies = [ - "serde", -] - -[[package]] -name = "metal" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" -dependencies = [ - "bitflags 2.6.0", - "block", - "core-graphics-types", - "foreign-types 0.5.0", - "log", - "objc", - "paste", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" -dependencies = [ - "adler32", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "mio" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.52.0", -] - -[[package]] -name = "monch" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b52c1b33ff98142aecea13138bd399b68aa7ab5d9546c300988c345004001eea" - -[[package]] -name = "multimap" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" - -[[package]] -name = "naga" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" -dependencies = [ - "arrayvec", - "bit-set", - "bitflags 2.6.0", - "codespan-reporting", - "hexf-parse", - "indexmap 2.7.0", - "log", - "num-traits", - "rustc-hash 1.1.0", - "serde", - "spirv", - "termcolor", - "thiserror 1.0.69", - "unicode-xid", -] - -[[package]] -name = "napi_sym" -version = "0.109.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b3ee1b2d30885de3ee82429b5aebe6f22b3eae5cb290cd8d6537a62212812b" -dependencies = [ - "quote", - "serde", - "serde_json", - "syn 2.0.101", -] - -[[package]] -name = "ndk-sys" -version = "0.5.0+25.2.9519653" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "netif" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29a01b9f018d6b7b277fef6c79fdbd9bf17bb2d1e298238055cafab49baa5ee" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "libc", -] - -[[package]] -name = "node_resolver" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e999e1cdbb49cdfa3f63ddd061c57205aa5f7be8f43bdbc4081c0f60d24d7d" -dependencies = [ - "anyhow", - "async-trait", - "boxed_error", - "deno_media_type", - "deno_package_json", - "deno_path_util", - "futures", - "lazy-regex", - "once_cell", - "path-clean", - "regex", - "serde_json", - "thiserror 1.0.69", - "tokio", - "url", -] - -[[package]] -name = "nom" -version = "5.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08959a387a676302eebf4ddbcbc611da04285579f76f88ee0506c63b1a61dd4b" -dependencies = [ - "memchr", - "version_check", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - -[[package]] -name = "notify" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" -dependencies = [ - "bitflags 2.6.0", - "crossbeam-channel", - "filetime", - "fsevent-sys", - "inotify", - "kqueue", - "libc", - "log", - "mio 0.8.11", - "walkdir", - "windows-sys 0.48.0", -] - -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", - "rand", - "serde", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand", - "serde", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "num-format" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" -dependencies = [ - "arrayvec", - "itoa", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.9", - "libc", -] - -[[package]] -name = "numbat" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5124c7a716bd197d4ad501237fa890771f69f38b34eb87f4514fdebf0cdcaf5b" -dependencies = [ - "codespan-reporting", - "heck 0.4.1", - "indexmap 2.7.0", - "itertools 0.12.1", - "jiff", - "libc", - "mendeleev", - "num-format", - "num-integer", - "num-rational", - "num-traits", - "numbat-exchange-rates", - "plotly", - "pretty_dtoa", - "rand", - "rust-embed", - "strfmt", - "strsim", - "thiserror 1.0.69", - "unicode-ident", - "unicode-width", - "walkdir", -] - -[[package]] -name = "numbat-exchange-rates" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd1e3c3e4f9f22d0d7cdcb413f01194f6506a302a9029d95deedcd1c25df7718" -dependencies = [ - "attohttpc", - "quick-xml 0.31.0", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - -[[package]] -name = "objc-sys" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" - -[[package]] -name = "objc2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" -dependencies = [ - "objc-sys", - "objc2-encode", -] - -[[package]] -name = "objc2-app-kit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" -dependencies = [ - "bitflags 2.6.0", - "block2", - "libc", - "objc2", - "objc2-core-data", - "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", -] - -[[package]] -name = "objc2-core-data" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" -dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-image" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" -dependencies = [ - "block2", - "objc2", - "objc2-foundation", - "objc2-metal", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "objc2-foundation" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" -dependencies = [ - "bitflags 2.6.0", - "block2", - "libc", - "objc2", -] - -[[package]] -name = "objc2-metal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" -dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" -dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", - "objc2-foundation", - "objc2-metal", -] - -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - -[[package]] -name = "object" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" -dependencies = [ - "memchr", -] - -[[package]] -name = "oid-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" -dependencies = [ - "asn1-rs", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "open" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ecd52f0b8d15c40ce4820aa251ed5de032e5d91fab27f7db2f40d42a8bdf69c" -dependencies = [ - "is-wsl", - "libc", - "pathdiff", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "opentelemetry" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab70038c28ed37b97d8ed414b6429d343a8bbf44c9f79ec854f3a643029ba6d7" -dependencies = [ - "futures-core", - "futures-sink", - "js-sys", - "pin-project-lite", - "thiserror 1.0.69", - "tracing", -] - -[[package]] -name = "opentelemetry-http" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a8a7f5f6ba7c1b286c2fbca0454eaba116f63bbe69ed250b642d36fbb04d80" -dependencies = [ - "async-trait", - "bytes", - "http 1.2.0", - "opentelemetry", -] - -[[package]] -name = "opentelemetry-otlp" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76" -dependencies = [ - "async-trait", - "futures-core", - "http 1.2.0", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-proto", - "opentelemetry_sdk", - "prost", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tonic", - "tracing", -] - -[[package]] -name = "opentelemetry-proto" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e05acbfada5ec79023c85368af14abd0b307c015e9064d249b2a950ef459a6" -dependencies = [ - "hex", - "opentelemetry", - "opentelemetry_sdk", - "prost", - "serde", - "tonic", -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc1b6902ff63b32ef6c489e8048c5e253e2e4a803ea3ea7e783914536eb15c52" - -[[package]] -name = "opentelemetry_sdk" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8" -dependencies = [ - "async-trait", - "futures-channel", - "futures-executor", - "futures-util", - "glob", - "opentelemetry", - "percent-encoding", - "rand", - "serde_json", - "thiserror 1.0.69", - "tracing", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ordered-multimap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" -dependencies = [ - "dlv-list", - "hashbrown 0.14.5", -] - -[[package]] -name = "os_info" -version = "3.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ca711d8b83edbb00b44d504503cd247c9c0bd8b0fa2694f2a1a3d8165379ce" -dependencies = [ - "log", - "serde", - "windows-sys 0.52.0", -] - -[[package]] -name = "os_pipe" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "outref" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" - -[[package]] -name = "outref" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" - -[[package]] -name = "p224" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c06436d66652bc2f01ade021592c80a2aad401570a18aa18b82e440d2b9aa1" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p521" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" -dependencies = [ - "base16ct", - "ecdsa", - "elliptic-curve", - "primeorder", - "rand_core", - "sha2", -] - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "password-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "path-clean" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" - -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.7.0", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher 0.3.11", -] - -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - -[[package]] -name = "pin-project" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "aes", - "cbc", - "der", - "pbkdf2", - "scrypt", - "sha2", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "pkcs5", - "rand_core", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - -[[package]] -name = "plist" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" -dependencies = [ - "base64 0.22.1", - "indexmap 2.7.0", - "quick-xml 0.32.0", - "serde", - "time", -] - -[[package]] -name = "plotly" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e1ffd11c8a6ef0b730b9d3e46ad2404f79905825cb20223fa0547434a2dff54" -dependencies = [ - "dyn-clone", - "erased-serde", - "once_cell", - "plotly_derive", - "rand", - "rinja", - "serde", - "serde_json", - "serde_repr", - "serde_with", -] - -[[package]] -name = "plotly_derive" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e940d8d8db30c6f4cc37dab9aab61f4c9cc1e6efb6d18902ab88fa09c03560" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "png" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "deflate", - "miniz_oxide 0.3.7", -] - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide 0.8.2", -] - -[[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 0.4.0", - "pin-project-lite", - "rustix", - "tracing", - "windows-sys 0.59.0", -] - -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "pretty_dtoa" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a239bcdfda2c685fda1add3b4695c06225f50075e3cfb5b954e91545587edff2" -dependencies = [ - "ryu_floating_decimal", -] - -[[package]] -name = "prettyplease" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" -dependencies = [ - "proc-macro2", - "syn 2.0.101", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-rules" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" -dependencies = [ - "proc-macro-rules-macros", - "proc-macro2", - "syn 2.0.101", -] - -[[package]] -name = "proc-macro-rules-macros" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207fffb0fe655d1d47f6af98cc2793405e85929bdbc420d685554ff07be27ac7" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "proc-macro2" -version = "1.0.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "profiling" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" -dependencies = [ - "quote", - "syn 2.0.101", -] - -[[package]] -name = "prost" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" -dependencies = [ - "heck 0.5.0", - "itertools 0.13.0", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 2.0.101", - "tempfile", -] - -[[package]] -name = "prost-derive" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" -dependencies = [ - "anyhow", - "itertools 0.13.0", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "prost-types" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" -dependencies = [ - "prost", -] - -[[package]] -name = "psm" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" -dependencies = [ - "cc", -] - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "qoi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quick-xml" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" -dependencies = [ - "memchr", -] - -[[package]] -name = "quick-xml" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" -dependencies = [ - "memchr", -] - -[[package]] -name = "quick-xml" -version = "0.36.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "range-alloc" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" - -[[package]] -name = "rav1e" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" -dependencies = [ - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools 0.12.1", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "once_cell", - "paste", - "profiling", - "rand", - "rand_chacha", - "simd_helpers", - "system-deps", - "thiserror 1.0.69", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error 2.0.1", - "rav1e", - "rayon", - "rgb", -] - -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "recvmsg" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" - -[[package]] -name = "redox_syscall" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror 1.0.69", -] - -[[package]] -name = "ref-cast" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error 1.2.3", -] - -[[package]] -name = "resvg" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958" -dependencies = [ - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rgb" -version = "0.8.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rinja" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5" -dependencies = [ - "humansize", - "itoa", - "percent-encoding", - "rinja_derive", - "serde", - "serde_json", -] - -[[package]] -name = "rinja_derive" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b" -dependencies = [ - "basic-toml", - "memchr", - "mime", - "mime_guess", - "proc-macro2", - "quote", - "rinja_parser", - "rustc-hash 2.1.0", - "serde", - "syn 2.0.101", -] - -[[package]] -name = "rinja_parser" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610" -dependencies = [ - "memchr", - "nom 7.1.3", - "serde", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest", -] - -[[package]] -name = "ron" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" -dependencies = [ - "base64 0.21.7", - "bitflags 2.6.0", - "serde", - "serde_derive", -] - -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - -[[package]] -name = "rsa" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[package]] -name = "rusqlite" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" -dependencies = [ - "bitflags 2.6.0", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", -] - -[[package]] -name = "rust-embed" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "shellexpand", - "syn 2.0.101", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" -dependencies = [ - "sha2", - "walkdir", -] - -[[package]] -name = "rust-ini" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hash" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver 1.0.24", -] - -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom 7.1.3", -] - -[[package]] -name = "rustix" -version = "0.38.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" -dependencies = [ - "bitflags 2.6.0", - "errno 0.3.10", - "libc", - "linux-raw-sys", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustls" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" -dependencies = [ - "log", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls" -version = "0.23.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" -dependencies = [ - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" - -[[package]] -name = "rustls-tokio-stream" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22557157d7395bc30727745b365d923f1ecc230c4c80b176545f3f4f08c46e33" -dependencies = [ - "futures", - "rustls 0.23.20", - "socket2", - "tokio", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" - -[[package]] -name = "rustyline" -version = "13.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" -dependencies = [ - "bitflags 2.6.0", - "cfg-if", - "clipboard-win", - "fd-lock", - "home", - "libc", - "log", - "memchr", - "nix", - "radix_trie", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "winapi", -] - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "ryu-js" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" - -[[package]] -name = "ryu_floating_decimal" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "700de91d5fd6091442d00fdd9ee790af6d4f0f480562b0f5a1e8f59e90aafe73" - -[[package]] -name = "saffron" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03fb9a628596fc7590eb7edbf7b0613287be78df107f5f97b118aad59fb2eea9" -dependencies = [ - "chrono", - "nom 5.1.3", -] - -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "password-hash", - "pbkdf2", - "salsa20", - "sha2", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "serdect", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.6.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "1.0.216" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.216" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "serde_json" -version = "1.0.133" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" -dependencies = [ - "indexmap 2.7.0", - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_v8" -version = "0.230.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a783242d2af51d6955cc04bf2b64adb643ab588b61e9573c908a69dabf8c2f" -dependencies = [ - "num-bigint", - "serde", - "smallvec", - "thiserror 1.0.69", - "v8", -] - -[[package]] -name = "serde_with" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.7.0", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "serdect" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" -dependencies = [ - "base16ct", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "shellexpand" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" -dependencies = [ - "dirs", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[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 = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core", -] - -[[package]] -name = "simd-abstraction" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" -dependencies = [ - "outref 0.1.0", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simd-json" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2bcf6c6e164e81bc7a5d49fc6988b3d515d9e8c07457d7b74ffb9324b9cd40" -dependencies = [ - "getrandom", - "halfbrown", - "ref-cast", - "serde", - "serde_json", - "simdutf8", - "value-trait", -] - -[[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] -name = "simplecss" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" -dependencies = [ - "log", -] - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "version_check", -] - -[[package]] -name = "sm3" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebb9a3b702d0a7e33bc4d85a14456633d2b165c2ad839c5fd9a8417c1ab15860" -dependencies = [ - "digest", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "smartstring" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" -dependencies = [ - "autocfg", - "static_assertions", - "version_check", -] - -[[package]] -name = "smithay-client-toolkit" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" -dependencies = [ - "bitflags 2.6.0", - "bytemuck", - "calloop", - "calloop-wayland-source", - "cursor-icon", - "libc", - "log", - "memmap2 0.9.5", - "pkg-config", - "rustix", - "thiserror 1.0.69", - "wayland-backend", - "wayland-client", - "wayland-csd-frame", - "wayland-cursor", - "wayland-protocols", - "wayland-protocols-wlr", - "wayland-scanner", - "xkbcommon", - "xkeysym", -] - -[[package]] -name = "socket2" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "sourcemap" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" -dependencies = [ - "base64-simd 0.7.0", - "bitvec", - "data-encoding", - "debugid", - "if_chain", - "rustc-hash 1.1.0", - "rustc_version 0.2.3", - "serde", - "serde_json", - "unicode-id-start", - "url", -] - -[[package]] -name = "sourcemap" -version = "9.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c4ea7042fd1a155ad95335b5d505ab00d5124ea0332a06c8390d200bb1a76a" -dependencies = [ - "base64-simd 0.7.0", - "bitvec", - "data-encoding", - "debugid", - "if_chain", - "rustc-hash 1.1.0", - "rustc_version 0.2.3", - "serde", - "serde_json", - "unicode-id-start", - "url", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spirv" -version = "0.3.0+sdk-1.3.268.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "stacker" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.59.0", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strfmt" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8348af2d9fc3258c8733b8d9d8db2e56f54b2363a4b5b81585c7875ed65e65" - -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -dependencies = [ - "float-cmp 0.9.0", -] - -[[package]] -name = "string_enum" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "stringcase" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04028eeb851ed08af6aba5caa29f2d59a13ed168cee4d6bd753aeefcf1d636b0" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.101", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "svgtypes" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794de53cc48eaabeed0ab6a3404a65f40b3e38c067e4435883a65d2aa4ca000e" -dependencies = [ - "kurbo", - "siphasher 1.0.1", -] - -[[package]] -name = "swc_allocator" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76aa0eb65c0f39f9b6d82a7e5192c30f7ac9a78f084a21f270de1d8c600ca388" -dependencies = [ - "bumpalo", - "hashbrown 0.14.5", - "ptr_meta", - "rustc-hash 1.1.0", - "triomphe", -] - -[[package]] -name = "swc_atoms" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6567e4e67485b3e7662b486f1565bdae54bd5b9d6b16b2ba1a9babb1e42125" -dependencies = [ - "hstr", - "once_cell", - "rustc-hash 1.1.0", - "serde", -] - -[[package]] -name = "swc_cached" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c" -dependencies = [ - "ahash", - "anyhow", - "dashmap", - "once_cell", - "regex", - "serde", -] - -[[package]] -name = "swc_common" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12d0a8eaaf1606c9207077d75828008cb2dfb51b095a766bd2b72ef893576e31" -dependencies = [ - "ast_node", - "better_scoped_tls", - "cfg-if", - "either", - "from_variant", - "new_debug_unreachable", - "num-bigint", - "once_cell", - "rustc-hash 1.1.0", - "serde", - "siphasher 0.3.11", - "sourcemap 9.1.2", - "swc_allocator", - "swc_atoms", - "swc_eq_ignore_macros", - "swc_visit", - "tracing", - "unicode-width", - "url", -] - -[[package]] -name = "swc_config" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" -dependencies = [ - "anyhow", - "indexmap 2.7.0", - "serde", - "serde_json", - "swc_cached", - "swc_config_macro", -] - -[[package]] -name = "swc_config_macro" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5f56139042c1a95b54f5ca48baa0e0172d369bcc9d3d473dad1de36bae8399" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "swc_ecma_ast" -version = "0.118.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f866d12e4d519052b92a0a86d1ac7ff17570da1272ca0c89b3d6f802cd79df" -dependencies = [ - "bitflags 2.6.0", - "is-macro", - "num-bigint", - "phf", - "scoped-tls", - "serde", - "string_enum", - "swc_atoms", - "swc_common", - "unicode-id-start", -] - -[[package]] -name = "swc_ecma_codegen" -version = "0.155.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7641608ef117cfbef9581a99d02059b522fcca75e5244fa0cbbd8606689c6f" -dependencies = [ - "memchr", - "num-bigint", - "once_cell", - "serde", - "sourcemap 9.1.2", - "swc_allocator", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_codegen_macros", - "tracing", -] - -[[package]] -name = "swc_ecma_codegen_macros" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859fabde36db38634f3fad548dd5e3410c1aebba1b67a3c63e67018fa57a0bca" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "swc_ecma_loader" -version = "0.49.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55fa3d55045b97894bfb04d38aff6d6302ac8a6a38e3bb3dfb0d20475c4974a9" -dependencies = [ - "anyhow", - "pathdiff", - "serde", - "swc_atoms", - "swc_common", - "tracing", -] - -[[package]] -name = "swc_ecma_parser" -version = "0.149.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683dada14722714588b56481399c699378b35b2ba4deb5c4db2fb627a97fb54b" -dependencies = [ - "either", - "new_debug_unreachable", - "num-bigint", - "num-traits", - "phf", - "serde", - "smallvec", - "smartstring", - "stacker", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "tracing", - "typed-arena", -] - -[[package]] -name = "swc_ecma_transforms_base" -version = "0.145.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f21494e75d0bd8ef42010b47cabab9caaed8f2207570e809f6f4eb51a710d1" -dependencies = [ - "better_scoped_tls", - "bitflags 2.6.0", - "indexmap 2.7.0", - "once_cell", - "phf", - "rustc-hash 1.1.0", - "serde", - "smallvec", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_parser", - "swc_ecma_utils", - "swc_ecma_visit", - "tracing", -] - -[[package]] -name = "swc_ecma_transforms_classes" -version = "0.134.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3d884594385bea9405a2e1721151470d9a14d3ceec5dd773c0ca6894791601" -dependencies = [ - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms_macros" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500a1dadad1e0e41e417d633b3d6d5de677c9e0d3159b94ba3348436cdb15aab" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "swc_ecma_transforms_proposal" -version = "0.179.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79938ff510fc647febd8c6c3ef4143d099fdad87a223680e632623d056dae2dd" -dependencies = [ - "either", - "rustc-hash 1.1.0", - "serde", - "smallvec", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_transforms_classes", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms_react" -version = "0.191.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" -dependencies = [ - "base64 0.21.7", - "dashmap", - "indexmap 2.7.0", - "once_cell", - "serde", - "sha1", - "string_enum", - "swc_allocator", - "swc_atoms", - "swc_common", - "swc_config", - "swc_ecma_ast", - "swc_ecma_parser", - "swc_ecma_transforms_base", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms_typescript" -version = "0.198.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15455da4768f97186c40523e83600495210c11825d3a44db43383fd81eace88d" -dependencies = [ - "ryu-js", - "serde", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_transforms_react", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_utils" -version = "0.134.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029eec7dd485923a75b5a45befd04510288870250270292fc2c1b3a9e7547408" -dependencies = [ - "indexmap 2.7.0", - "num_cpus", - "once_cell", - "rustc-hash 1.1.0", - "ryu-js", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_visit", - "tracing", - "unicode-id", -] - -[[package]] -name = "swc_ecma_visit" -version = "0.104.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1c6802e68e51f336e8bc9644e9ff9da75d7da9c1a6247d532f2e908aa33e81" -dependencies = [ - "new_debug_unreachable", - "num-bigint", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_visit", - "tracing", -] - -[[package]] -name = "swc_eq_ignore_macros" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "swc_macros_common" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "swc_visit" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceb044142ba2719ef9eb3b6b454fce61ab849eb696c34d190f04651955c613d" -dependencies = [ - "either", - "new_debug_unreachable", -] - -[[package]] -name = "swc_visit_macros" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92807d840959f39c60ce8a774a3f83e8193c658068e6d270dbe0a05e40e90b41" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "sys-locale" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" -dependencies = [ - "libc", -] - -[[package]] -name = "system-deps" -version = "6.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" -dependencies = [ - "cfg-expr", - "heck 0.5.0", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - -[[package]] -name = "tempfile" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" -dependencies = [ - "cfg-if", - "fastrand", - "once_cell", - "rustix", - "windows-sys 0.59.0", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "text_lines" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd5828de7deaa782e1dd713006ae96b3bee32d3279b79eb67ecf8072c059bcf" -dependencies = [ - "serde", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" -dependencies = [ - "thiserror-impl 2.0.8", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "tiff" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" -dependencies = [ - "flate2", - "jpeg-decoder", - "weezl", -] - -[[package]] -name = "time" -version = "0.3.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tiny-skia" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "log", - "png 0.17.16", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio 1.0.3", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-eld" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9166030f05d6bc5642bdb8f8c2be31eb3c02cd465d662bcdc2df82d4aa41a584" -dependencies = [ - "hdrhistogram", - "tokio", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "tokio-metrics" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eace09241d62c98b7eeb1107d4c5c64ca3bd7da92e8c218c153ab3a78f9be112" -dependencies = [ - "futures-util", - "pin-project-lite", - "tokio", - "tokio-stream", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" -dependencies = [ - "rustls 0.23.20", - "tokio", -] - -[[package]] -name = "tokio-socks" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" -dependencies = [ - "either", - "futures-util", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "futures-util", - "hashbrown 0.14.5", - "pin-project-lite", - "slab", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" -dependencies = [ - "indexmap 2.7.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tonic" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.22.1", - "bytes", - "h2 0.4.7", - "http 1.2.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.5.2", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "prost", - "socket2", - "tokio", - "tokio-stream", - "tower 0.4.13", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tonic-build" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" -dependencies = [ - "prettyplease", - "proc-macro2", - "prost-build", - "prost-types", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" -dependencies = [ - "async-compression", - "bitflags 2.6.0", - "bytes", - "futures-core", - "http 1.2.0", - "http-body 1.0.1", - "http-body-util", - "pin-project-lite", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.41" -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.101", -] - -[[package]] -name = "tracing-core" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" -dependencies = [ - "once_cell", -] - -[[package]] -name = "triomphe" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" -dependencies = [ - "serde", - "stable_deref_trait", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "rand", - "static_assertions", -] - -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - -[[package]] -name = "typed-path" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41713888c5ccfd99979fcd1afd47b71652e331b3d4a0e19d30769e80fec76cce" - -[[package]] -name = "typeid" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-ucd-ident" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - -[[package]] -name = "unicase" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" - -[[package]] -name = "unicode-id" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" - -[[package]] -name = "unicode-id-start" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" - -[[package]] -name = "unicode-ident" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "urlpattern" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" -dependencies = [ - "regex", - "serde", - "unic-ucd-ident", - "url", -] - -[[package]] -name = "usvg" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447e703d7223b067607655e625e0dbca80822880248937da65966194c4864e6" -dependencies = [ - "base64 0.22.1", - "data-url", - "flate2", - "imagesize", - "kurbo", - "log", - "pico-args", - "roxmltree", - "simplecss", - "siphasher 1.0.1", - "strict-num", - "svgtypes", - "tiny-skia-path", - "xmlwriter", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "uuid" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" -dependencies = [ - "getrandom", - "serde", -] - -[[package]] -name = "v8" -version = "130.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee0be58935708fa4d7efb970c6cf9f2d9511d24ee24246481a65b6ee167348d" -dependencies = [ - "bindgen", - "bitflags 2.6.0", - "fslock", - "gzip-header", - "home", - "miniz_oxide 0.7.4", - "once_cell", - "paste", - "which 6.0.3", -] - -[[package]] -name = "v8_valueserializer" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97599c400fc79925922b58303e98fcb8fa88f573379a08ddb652e72cbd2e70f6" -dependencies = [ - "bitflags 2.6.0", - "encoding_rs", - "indexmap 2.7.0", - "num-bigint", - "serde", - "thiserror 1.0.69", - "wtf8", -] - -[[package]] -name = "v_frame" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] - -[[package]] -name = "value-trait" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9170e001f458781e92711d2ad666110f153e4e50bfd5cbd02db6547625714187" -dependencies = [ - "float-cmp 0.10.0", - "halfbrown", - "itoa", - "ryu", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version-compare" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "virtue" -version = "0.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314" - -[[package]] -name = "vsimd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.101", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" - -[[package]] -name = "wasm_dep_analyzer" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f270206a91783fd90625c8bb0d8fbd459d0b1d1bf209b656f713f01ae7c04b8" -dependencies = [ - "thiserror 1.0.69", -] - -[[package]] -name = "wayland-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" -dependencies = [ - "cc", - "downcast-rs", - "rustix", - "scoped-tls", - "smallvec", - "wayland-sys", -] - -[[package]] -name = "wayland-client" -version = "0.31.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" -dependencies = [ - "bitflags 2.6.0", - "rustix", - "wayland-backend", - "wayland-scanner", -] - -[[package]] -name = "wayland-csd-frame" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" -dependencies = [ - "bitflags 2.6.0", - "cursor-icon", - "wayland-backend", -] - -[[package]] -name = "wayland-cursor" -version = "0.31.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b08bc3aafdb0035e7fe0fdf17ba0c09c268732707dca4ae098f60cb28c9e4c" -dependencies = [ - "rustix", - "wayland-client", - "xcursor", -] - -[[package]] -name = "wayland-protocols" -version = "0.32.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" -dependencies = [ - "bitflags 2.6.0", - "wayland-backend", - "wayland-client", - "wayland-scanner", - "wayland-server", -] - -[[package]] -name = "wayland-protocols-wlr" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" -dependencies = [ - "bitflags 2.6.0", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", - "wayland-server", -] - -[[package]] -name = "wayland-scanner" -version = "0.31.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" -dependencies = [ - "proc-macro2", - "quick-xml 0.36.2", - "quote", -] - -[[package]] -name = "wayland-server" -version = "0.31.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89532cc712a2adb119eb4d09694b402576052254d0bb284f82ac1c47fb786ad" -dependencies = [ - "bitflags 2.6.0", - "downcast-rs", - "io-lifetimes", - "rustix", - "wayland-backend", - "wayland-scanner", -] - -[[package]] -name = "wayland-sys" -version = "0.31.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" -dependencies = [ - "dlib", - "log", - "pkg-config", -] - -[[package]] -name = "web-sys" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-root-certs" -version = "0.26.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd5da49bdf1f30054cfe0b8ce2958b8fbeb67c4d82c8967a598af481bef255c" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webpki-roots" -version = "0.26.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "weezl" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" - -[[package]] -name = "wgpu-core" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39" -dependencies = [ - "arrayvec", - "bit-vec", - "bitflags 2.6.0", - "cfg_aliases", - "codespan-reporting", - "document-features", - "indexmap 2.7.0", - "log", - "naga", - "once_cell", - "parking_lot", - "profiling", - "raw-window-handle", - "ron", - "rustc-hash 1.1.0", - "serde", - "smallvec", - "thiserror 1.0.69", - "web-sys", - "wgpu-hal", - "wgpu-types", -] - -[[package]] -name = "wgpu-hal" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222" -dependencies = [ - "android_system_properties", - "arrayvec", - "ash", - "bit-set", - "bitflags 2.6.0", - "block", - "cfg_aliases", - "core-graphics-types", - "d3d12", - "glow", - "glutin_wgl_sys", - "gpu-alloc", - "gpu-descriptor", - "js-sys", - "khronos-egl", - "libc", - "libloading 0.8.6", - "log", - "metal", - "naga", - "ndk-sys", - "objc", - "once_cell", - "parking_lot", - "profiling", - "range-alloc", - "raw-window-handle", - "rustc-hash 1.1.0", - "smallvec", - "thiserror 1.0.69", - "wasm-bindgen", - "web-sys", - "wgpu-types", - "winapi", -] - -[[package]] -name = "wgpu-types" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" -dependencies = [ - "bitflags 2.6.0", - "js-sys", - "serde", - "web-sys", -] - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "which" -version = "6.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" -dependencies = [ - "either", - "home", - "rustix", - "winsafe", -] - -[[package]] -name = "which" -version = "7.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a9e33648339dc1642b0e36e21b3385e6148e289226f657c809dee59df5028" -dependencies = [ - "either", - "env_home", - "rustix", - "winsafe", -] - -[[package]] -name = "whoami" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" -dependencies = [ - "redox_syscall", - "wasite", - "web-sys", -] - -[[package]] -name = "widestring" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "winsafe" -version = "0.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "wtf8" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c01ae8492c38f52376efd3a17d0994b6bcf3df1e39c0226d458b7d81670b2a06" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x11rb" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" -dependencies = [ - "gethostname", - "rustix", - "x11rb-protocol", -] - -[[package]] -name = "x11rb-protocol" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" - -[[package]] -name = "x25519-dalek" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" -dependencies = [ - "curve25519-dalek", - "rand_core", - "serde", - "zeroize", -] - -[[package]] -name = "x509-parser" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom 7.1.3", - "oid-registry", - "rusticata-macros", - "thiserror 1.0.69", - "time", -] - -[[package]] -name = "xcursor" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" - -[[package]] -name = "xdg" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" - -[[package]] -name = "xkbcommon" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" -dependencies = [ - "libc", - "memmap2 0.8.0", - "xkeysym", -] - -[[package]] -name = "xkeysym" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "xml-rs" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" - -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", - "synstructure 0.13.1", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "zerofrom" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", - "synstructure 0.13.1", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "zune-jpeg" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" -dependencies = [ - "zune-core", -] diff --git a/rust/plugin_runtime/Cargo.toml b/rust/plugin_runtime/Cargo.toml index 8d5c307..6f60c7c 100644 --- a/rust/plugin_runtime/Cargo.toml +++ b/rust/plugin_runtime/Cargo.toml @@ -3,12 +3,6 @@ name = "gauntlet-plugin-runtime" version = "0.1.0" edition.workspace = true -[workspace.package] -edition = "2024" - -[lib] -crate-type = ["rlib"] - [dependencies] # workspaces gauntlet-common = { path = "../common" } @@ -17,21 +11,21 @@ gauntlet-utils = { path = "../utils" } gauntlet-common-plugin-runtime = { path = "../common_plugin_runtime" } # shared -anyhow = { version = "1", features = ["backtrace"] } -tracing = { version = "0.1" } -tokio = { version = "1.42" } -tokio-util = "0.7" -serde = { version = "1.0", features = ["derive"] } -bincode = { version = "2.0.0-rc.3" } -indexmap = { version = "2.1", features = ["serde"] } -regex = { version = "1.9.3" } -futures = { version = "0.3.31" } -image = { version = "0.25" } -once_cell = { version = "1.19" } -bytes = { version = "1.6.0" } -walkdir = { version = "2.4.0" } -typed-path = { version = "0.10.0" } -interprocess = { version = "2.2.2", features = ["tokio"] } +anyhow.workspace = true +tracing.workspace = true +tokio.workspace = true +tokio-util.workspace = true +serde.workspace = true +bincode.workspace = true +indexmap.workspace = true +regex.workspace = true +futures.workspace = true +image.workspace = true +once_cell.workspace = true +bytes.workspace = true +walkdir.workspace = true +typed-path.workspace = true +interprocess.workspace = true # other deno_core = { version = "0.321.0" } # deno 2.1.1 @@ -71,6 +65,3 @@ libc = "0.2" scenario_runner = [] release = [] -[patch.crates-io] -# NOTE https://github.com/ipetkov/crane/issues/336 -libffi-sys = { git = "https://github.com/tov/libffi-rs", rev = "d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b" } diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index 26681ce..05cb58f 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -8,6 +8,7 @@ gauntlet-common.workspace = true gauntlet-utils.workspace = true gauntlet-client.workspace = true gauntlet-common-plugin-runtime.workspace = true +gauntlet-plugin-runtime.workspace = true gauntlet-scenario-runner = { workspace = true, optional = true } # shared @@ -44,8 +45,8 @@ dark-light = "1.1.1" schemars = "0.8" [features] -release = ["gauntlet-common/release"] -scenario_runner = ["dep:gauntlet-scenario-runner", "gauntlet-common/scenario_runner"] +release = ["gauntlet-common/release", "gauntlet-plugin-runtime/release"] +scenario_runner = ["dep:gauntlet-scenario-runner", "gauntlet-common/scenario_runner", "gauntlet-plugin-runtime/scenario_runner"] [build-dependencies] vergen-gitcl = { version = "1.0", features = ["build", "cargo"] } diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index fdd1bce..a32610d 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -42,7 +42,6 @@ pub fn start(minimized: bool) { register_panic_hook(std::env::var(PLUGIN_UUID_ENV).ok()); if let Ok(socket_name) = std::env::var(PLUGIN_CONNECT_ENV) { - // this is not actually unresolved item, see .cargo/config.toml gauntlet_plugin_runtime::run_plugin_runtime(socket_name); return; diff --git a/rust/utils_macros/Cargo.toml b/rust/utils_macros/Cargo.toml index 99b628f..ffd534e 100644 --- a/rust/utils_macros/Cargo.toml +++ b/rust/utils_macros/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "gauntlet-utils-macros" version = "0.1.0" -edition = "2024" +edition = "2021" [lib] proc-macro = true From b0fdaa6b9eb3509e6cba1130db45979d4567fe6f Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 25 May 2025 15:00:29 +0200 Subject: [PATCH 08/91] Switch edition 2024 again after revert --- Cargo.toml | 2 +- rust/manifest_schema/src/main.rs | 1 - rust/scenario_runner/src/frontend_mock.rs | 4 ++-- rust/utils_macros/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e915712..ca03a94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ members = [ ] [workspace.package] -edition = "2021" +edition = "2024" [workspace.dependencies] # iced diff --git a/rust/manifest_schema/src/main.rs b/rust/manifest_schema/src/main.rs index 1ec6401..c996a0c 100644 --- a/rust/manifest_schema/src/main.rs +++ b/rust/manifest_schema/src/main.rs @@ -1,4 +1,3 @@ -use std::io::Write; use std::path::PathBuf; use gauntlet_server::plugins::plugin_manifest::PluginManifest; diff --git a/rust/scenario_runner/src/frontend_mock.rs b/rust/scenario_runner/src/frontend_mock.rs index ad5ccdc..991406e 100644 --- a/rust/scenario_runner/src/frontend_mock.rs +++ b/rust/scenario_runner/src/frontend_mock.rs @@ -57,8 +57,8 @@ pub async fn start_scenario_runner_frontend( println!("backend started"); - let mut backend_for_frontend_client = BackendForFrontendApiProxy::new(backend_sender); - let mut backend_client = GrpcBackendApi::new().await?; + let backend_for_frontend_client = BackendForFrontendApiProxy::new(backend_sender); + let backend_client = GrpcBackendApi::new().await?; println!("saving local plugin"); diff --git a/rust/utils_macros/Cargo.toml b/rust/utils_macros/Cargo.toml index ffd534e..99b628f 100644 --- a/rust/utils_macros/Cargo.toml +++ b/rust/utils_macros/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "gauntlet-utils-macros" version = "0.1.0" -edition = "2021" +edition = "2024" [lib] proc-macro = true From c0f61f444db55f83de32e378e985d6a4ce7c51f3 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 27 May 2025 22:44:24 +0200 Subject: [PATCH 09/91] WIP Migration from sqlx to rusqlite. No sql migrations yet. Doesn't compile --- Cargo.lock | 3174 ++++++++--------- Cargo.toml | 4 - rust/common/Cargo.toml | 1 - rust/plugin_runtime/Cargo.toml | 10 +- rust/plugin_runtime/src/assets.rs | 6 +- rust/plugin_runtime/src/clipboard.rs | 14 +- rust/plugin_runtime/src/deno.rs | 85 +- .../src/entrypoint_generators.rs | 6 +- rust/plugin_runtime/src/events.rs | 4 +- rust/plugin_runtime/src/lib.rs | 4 +- rust/plugin_runtime/src/logs.rs | 11 +- rust/plugin_runtime/src/permissions.rs | 4 +- .../src/plugins/applications/linux/mod.rs | 15 +- .../plugins/applications/linux/wayland/mod.rs | 8 +- .../src/plugins/applications/linux/x11.rs | 7 +- rust/plugin_runtime/src/plugins/numbat.rs | 8 +- rust/plugin_runtime/src/plugins/settings.rs | 12 +- rust/plugin_runtime/src/preferences.rs | 12 +- rust/plugin_runtime/src/search.rs | 3 +- rust/plugin_runtime/src/ui.rs | 22 +- rust/server/Cargo.toml | 5 +- rust/server/src/plugins/data_db_repository.rs | 1166 +++--- rust/server/src/plugins/js.rs | 56 +- rust/server/src/plugins/loader.rs | 78 +- rust/server/src/plugins/mod.rs | 57 +- rust/server/src/plugins/runtime.rs | 1 - rust/server/src/plugins/settings.rs | 40 +- rust/utils_macros/src/boundary_gen.rs | 453 +++ rust/utils_macros/src/lib.rs | 454 +-- rust/utils_macros/src/rusqlite.rs | 110 + 30 files changed, 3042 insertions(+), 2788 deletions(-) delete mode 100644 rust/server/src/plugins/runtime.rs create mode 100644 rust/utils_macros/src/boundary_gen.rs create mode 100644 rust/utils_macros/src/rusqlite.rs diff --git a/Cargo.lock b/Cargo.lock index 0fc1077..f0fe1b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,16 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "ab_glyph" version = "0.2.29" @@ -37,12 +27,6 @@ dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -67,9 +51,9 @@ dependencies = [ [[package]] name = "aead-gcm-stream" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4947a169074c7e038fa43051d1c4e073f4488b0e4b0a30658f1e1a1b06449ce8" +checksum = "e70c8dec860340effb00f6945c49c0daaa6dac963602750db862eabb74bf7886" dependencies = [ "aead", "aes", @@ -119,7 +103,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", ] @@ -131,7 +115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -180,7 +164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.6.0", + "bitflags 2.9.1", "cc", "cesu8", "jni", @@ -345,15 +329,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" -[[package]] -name = "ash" -version = "0.37.3+1.3.251" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" -dependencies = [ - "libloading 0.7.4", -] - [[package]] name = "ash" version = "0.38.0+1.3.281" @@ -402,18 +377,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "ast_node" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9184f2b369b3e8625712493c89b785881f27eedc6cde480a81883cef78868b2" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - [[package]] name = "async-broadcast" version = "0.7.1" @@ -506,6 +469,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-once-cell" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" + [[package]] name = "async-process" version = "2.3.0" @@ -616,15 +585,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "atoi" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" -dependencies = [ - "num-traits", -] - [[package]] name = "atomic-waker" version = "1.1.2" @@ -731,6 +691,12 @@ dependencies = [ "tower-service", ] +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + [[package]] name = "backtrace" version = "0.3.74" @@ -740,7 +706,7 @@ dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide 0.8.2", + "miniz_oxide 0.8.8", "object", "rustc-demangle", "windows-targets 0.52.6", @@ -804,15 +770,6 @@ dependencies = [ "serde", ] -[[package]] -name = "better_scoped_tls" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297b153aa5e573b5863108a6ddc9d5c968bd0b20e75cc614ee9821d2f45679c7" -dependencies = [ - "scoped-tls", -] - [[package]] name = "bincode" version = "1.3.3" @@ -843,11 +800,11 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.70.1" +version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -856,35 +813,20 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash 2.1.0", "shlex", "syn 2.0.101", ] -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec 0.6.3", -] - [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec 0.8.0", + "bit-vec", ] -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bit-vec" version = "0.8.0" @@ -905,9 +847,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" dependencies = [ "serde", ] @@ -1069,9 +1011,9 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.20.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" dependencies = [ "bytemuck_derive", ] @@ -1135,7 +1077,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cairo-sys-rs", "glib", "libc", @@ -1160,7 +1102,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "log", "polling", "rustix", @@ -1174,7 +1116,7 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10929724661d1c43856fd87c7a127ae944ec55579134fb485e4136fb6a46fdcb" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "polling", "rustix", "slab", @@ -1214,6 +1156,28 @@ dependencies = [ "serde", ] +[[package]] +name = "capacity_builder" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f2d24a6dcf0cd402a21b65d35340f3a49ff3475dc5fdac91d22d2733e6641c6" +dependencies = [ + "capacity_builder_macros", + "ecow", + "hipstr", + "itoa", +] + +[[package]] +name = "capacity_builder_macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" +dependencies = [ + "quote", + "syn 2.0.101", +] + [[package]] name = "cargo-platform" version = "0.1.9" @@ -1234,7 +1198,7 @@ dependencies = [ "semver 1.0.24", "serde", "serde_json", - "thiserror 2.0.8", + "thiserror 2.0.12", ] [[package]] @@ -1248,9 +1212,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.5" +version = "1.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" +checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" dependencies = [ "jobserver", "libc", @@ -1285,7 +1249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", - "target-lexicon", + "target-lexicon 0.12.16", ] [[package]] @@ -1504,7 +1468,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom", + "getrandom 0.2.15", "once_cell", "tiny-keccak", ] @@ -1591,7 +1555,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "core-foundation 0.10.0", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -1615,7 +1579,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "core-foundation 0.10.0", "libc", ] @@ -1625,7 +1589,7 @@ name = "cosmic-protocols" version = "0.1.0" source = "git+https://github.com/pop-os/cosmic-protocols.git#d218c76b58c7a3b20dd5e7943f93fc306a1b81b8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols 0.32.5", @@ -1640,7 +1604,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "fontdb 0.16.2", "log", "rangemap", @@ -1667,19 +1631,127 @@ dependencies = [ ] [[package]] -name = "crc" -version = "3.2.1" +name = "cranelift" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "a71de5e59f616d79d14d2c71aa2799ce898241d7f10f7e64a4997014b4000a28" dependencies = [ - "crc-catalog", + "cranelift-codegen", + "cranelift-frontend", + "cranelift-module", ] [[package]] -name = "crc-catalog" -version = "2.4.0" +name = "cranelift-bforest" +version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "e15d04a0ce86cb36ead88ad68cf693ffd6cda47052b9e0ac114bc47fd9cd23c4" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-bitset" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c6e3969a7ce267259ce244b7867c5d3bc9e65b0a87e81039588dfdeaede9f34" + +[[package]] +name = "cranelift-codegen" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c22032c4cb42558371cf516bb47f26cdad1819d3475c133e93c49f50ebf304e" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli", + "hashbrown 0.14.5", + "log", + "regalloc2", + "rustc-hash 2.1.0", + "serde", + "smallvec", + "target-lexicon 0.13.2", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c904bc71c61b27fc57827f4a1379f29de64fe95653b620a3db77d59655eee0b8" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40180f5497572f644ce88c255480981ae2ec1d7bb4d8e0c0136a13b87a2f2ceb" + +[[package]] +name = "cranelift-control" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d132c6d0bd8a489563472afc171759da0707804a65ece7ceb15a8c6d7dd5ef" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d0d9618275474fbf679dd018ac6e009acbd6ae6850f6a67be33fb3b00b323" +dependencies = [ + "cranelift-bitset", +] + +[[package]] +name = "cranelift-frontend" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fac41e16729107393174b0c9e3730fb072866100e1e64e80a1a963b2e484d57" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon 0.13.2", +] + +[[package]] +name = "cranelift-isle" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca20d576e5070044d0a72a9effc2deacf4d6aa650403189d8ea50126483944d" + +[[package]] +name = "cranelift-module" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d55612bebcf16ff7306c8a6f5bdb6d45662b8aa1ee058ecce8807ad87db719b" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", +] + +[[package]] +name = "cranelift-native" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dee82f3f1f2c4cba9177f1cc5e350fe98764379bcd29340caa7b01f85076c7" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon 0.13.2", +] [[package]] name = "crc32fast" @@ -1718,15 +1790,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -1746,7 +1809,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -1758,7 +1821,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "typenum", ] @@ -1810,17 +1873,6 @@ dependencies = [ "syn 2.0.101", ] -[[package]] -name = "d3d12" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" -dependencies = [ - "bitflags 2.6.0", - "libloading 0.8.6", - "winapi", -] - [[package]] name = "dark-light" version = "1.1.1" @@ -1893,9 +1945,9 @@ checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "data-url" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b319d1b62ffbd002e057f36bebd1f42b9f97927c9577461d855f3513c4289f" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "dconf_rs" @@ -1923,116 +1975,152 @@ dependencies = [ "byteorder", ] -[[package]] -name = "deno_ast" -version = "0.43.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d00b724e06d2081a141ec1155756a0b465d413d8e2a7515221f61d482eb2ee" -dependencies = [ - "base64 0.21.7", - "deno_media_type", - "deno_terminal 0.1.1", - "dprint-swc-ext", - "once_cell", - "percent-encoding", - "serde", - "sourcemap 9.1.2", - "swc_atoms", - "swc_common", - "swc_config", - "swc_config_macro", - "swc_ecma_ast", - "swc_ecma_codegen", - "swc_ecma_codegen_macros", - "swc_ecma_loader", - "swc_ecma_parser", - "swc_ecma_transforms_base", - "swc_ecma_transforms_classes", - "swc_ecma_transforms_macros", - "swc_ecma_transforms_proposal", - "swc_ecma_transforms_react", - "swc_ecma_transforms_typescript", - "swc_ecma_utils", - "swc_ecma_visit", - "swc_eq_ignore_macros", - "swc_macros_common", - "swc_visit", - "swc_visit_macros", - "text_lines", - "thiserror 1.0.69", - "unicode-width", - "url", -] - [[package]] name = "deno_broadcast_channel" -version = "0.173.0" +version = "0.199.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348ecdacfdd262e6b2f9740d07a41e8f4d79d06a670378a060515d0208495c9f" +checksum = "4e47997aae2d20622f7c6906c2d6682a45b0807b6932928b9c9d91fe8744c6c1" dependencies = [ "async-trait", "deno_core", - "thiserror 1.0.69", + "deno_error", + "deno_features", + "thiserror 2.0.12", "tokio", "uuid", ] [[package]] name = "deno_cache" -version = "0.111.0" +version = "0.137.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6e35cb122e56c22149652327c90c563790ddcef24ea1fc77454e193131318e" +checksum = "8ef0955e60153b9f863d82956e94dd34a55cb28a4693ccd0c9103da094d425b2" dependencies = [ + "async-stream", "async-trait", + "base64 0.22.1", + "bytes", + "chrono", "deno_core", + "deno_error", + "futures", + "http 1.2.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "log", "rusqlite", "serde", "sha2", - "thiserror 1.0.69", + "slab", + "thiserror 2.0.12", "tokio", + "tokio-util", +] + +[[package]] +name = "deno_cache_dir" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "869a62459ded73382e018c7c58a07df170ba5f5befb67e18ee10494e769efe2a" +dependencies = [ + "async-trait", + "base32", + "base64 0.21.7", + "boxed_error", + "cache_control", + "chrono", + "data-url", + "deno_error", + "deno_media_type", + "deno_path_util", + "http 1.2.0", + "indexmap 2.9.0", + "log", + "once_cell", + "parking_lot 0.12.3", + "serde", + "serde_json", + "sha2", + "sys_traits", + "thiserror 1.0.69", + "url", ] [[package]] name = "deno_canvas" -version = "0.48.0" +version = "0.74.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bbfd1437bc01ab775b1a60e3061bbf2e9517e31fb5eedf89b2b703104c835e6" +checksum = "293ca44d7b647d50337f517cac34f5c658a719a1d9be570d2bfc779c30823aec" dependencies = [ + "bytemuck", "deno_core", - "deno_webgpu", - "image 0.24.9", + "deno_error", + "image 0.25.5", + "lcms2", + "num-traits", + "thiserror 2.0.12", +] + +[[package]] +name = "deno_config" +version = "0.54.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a3ff33a35a2e995bfea372cbe9fe0990a735e27988a787277a1c6ee15d1b1a" +dependencies = [ + "boxed_error", + "capacity_builder", + "deno_error", + "deno_package_json", + "deno_path_util", + "deno_semver", + "glob", + "ignore", + "import_map", + "indexmap 2.9.0", + "jsonc-parser", + "log", + "percent-encoding", + "phf", "serde", - "thiserror 1.0.69", + "serde_json", + "sys_traits", + "thiserror 2.0.12", + "url", ] [[package]] name = "deno_console" -version = "0.179.0" +version = "0.205.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e09f2bbb2d842329b602da25dbab5cd4a342f9a8adcb7c02509fc322f796e79" +checksum = "f18e4a73bc0dd2a30dfbc14d8c2eb9b7347b7616acb6810d71e7912385c34f89" dependencies = [ "deno_core", ] [[package]] name = "deno_core" -version = "0.321.0" +version = "0.347.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd2a54cda74cdc187d5fc2d23370a45cf09f912caf566dd1cd24a50157d809c7" +checksum = "d75ae5562f6ad750bc2007e7b1032ae37115a83fe58b6fbc77331c47744956cc" dependencies = [ "anyhow", + "az", "bincode 1.3.3", - "bit-set 0.5.3", - "bit-vec 0.6.3", + "bit-set", + "bit-vec", "bytes", + "capacity_builder", "cooked-waker", "deno_core_icudata", + "deno_error", "deno_ops", + "deno_path_util", "deno_unsync", "futures", - "indexmap 2.7.0", + "indexmap 2.9.0", "libc", - "memoffset", "parking_lot 0.12.3", "percent-encoding", "pin-project", @@ -2040,8 +2128,9 @@ dependencies = [ "serde_json", "serde_v8", "smallvec", - "sourcemap 8.0.1", + "sourcemap", "static_assertions", + "thiserror 2.0.12", "tokio", "url", "v8", @@ -2056,35 +2145,38 @@ checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" [[package]] name = "deno_cron" -version = "0.59.0" +version = "0.85.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f936f036e9e3f88205db8efd0ec68c65efb47bc0cbe4b715bafecd6e9c407931" +checksum = "fa710e70d29c6951f865e677cf0a080caa2b838b70d1847a43b1cfc52b622db0" dependencies = [ - "anyhow", "async-trait", "chrono", "deno_core", + "deno_error", + "deno_features", "saffron", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", ] [[package]] name = "deno_crypto" -version = "0.193.0" +version = "0.219.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b582f30887c7c0902b4445c64d7c8b98d0043ec547c44de8de26104b093e1be" +checksum = "7608cfcd00abfe7452930b8480afdb3fa5fe28dbbafa4463b083b8322243fefe" dependencies = [ "aes", "aes-gcm", "aes-kw", - "base64 0.21.7", + "base64 0.22.1", "cbc", "const-oid", "ctr", "curve25519-dalek", "deno_core", + "deno_error", "deno_web", + "ecdsa", "ed448-goldilocks", "elliptic-curve", "num-traits", @@ -2092,40 +2184,79 @@ dependencies = [ "p256", "p384", "p521", - "rand", + "rand 0.8.5", "ring", "rsa", - "sec1", "serde", "serde_bytes", "sha1", "sha2", "signature", "spki", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "uuid", "x25519-dalek", ] [[package]] -name = "deno_fetch" -version = "0.203.0" +name = "deno_error" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18e66bd3bf786e24a8b8bdc97049fa82957b095a5fd1e142545c5a7cdd2272a" +checksum = "19fae9fe305307b5ef3ee4e8244c79cffcca421ab0ce8634dea0c6b1342f220f" dependencies = [ - "base64 0.21.7", + "deno_error_macro", + "libc", + "serde", + "serde_json", + "tokio", + "url", +] + +[[package]] +name = "deno_error_macro" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5abb2556e91848b66f562451fcbcdee2a3b7c88281828908dcf7cca355f5d997" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "deno_features" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf0bffbb52e0ad53c50225cdf0c20b24501036c3948264a049487fc5e5c40f57" +dependencies = [ + "deno_core", + "serde", + "serde_json", +] + +[[package]] +name = "deno_fetch" +version = "0.229.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "169ba4dc4ece5de4994dc9c5fdd30f7a2e019313eaf9911aa8db0919fe9e6495" +dependencies = [ + "base64 0.22.1", "bytes", "data-url", "deno_core", + "deno_error", + "deno_fs", + "deno_path_util", "deno_permissions", "deno_tls", "dyn-clone", "error_reporter", + "h2 0.4.7", "hickory-resolver", "http 1.2.0", "http-body-util", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-rustls", "hyper-util", "ipnet", @@ -2133,48 +2264,53 @@ dependencies = [ "rustls-webpki", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tokio-rustls", "tokio-socks", "tokio-util", - "tower 0.4.13", + "tower 0.5.2", "tower-http", "tower-service", ] [[package]] name = "deno_ffi" -version = "0.166.0" +version = "0.192.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6d2f13ebfa93833446abeb3bd1836fdf86bcb96678276b21a0622146f42284" +checksum = "d9836a034424a050372548a34dd4f4305aa2e677980e9c3b4be7e49161dcefba" dependencies = [ + "cranelift", + "cranelift-native", "deno_core", + "deno_error", "deno_permissions", + "denort_helper", "dlopen2 0.6.1", - "dynasmrt", "libffi", "libffi-sys", "log", + "memmap2 0.9.5", "num-bigint", "serde", "serde-value", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "winapi", ] [[package]] name = "deno_fs" -version = "0.89.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f53829328c344736d7fdda44733057299536f3379513cdcd258823ef273540ec" +checksum = "df413f816b1cfd10a0cd67da0aa0ad421bb4b45610da362202c004a39f61416b" dependencies = [ "async-trait", "base32", "boxed_error", "deno_core", + "deno_error", "deno_io", "deno_path_util", "deno_permissions", @@ -2182,38 +2318,41 @@ dependencies = [ "junction", "libc", "nix 0.27.1", - "rand", + "rand 0.8.5", "rayon", "serde", - "thiserror 1.0.69", + "thiserror 2.0.12", "winapi", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "deno_http" -version = "0.177.0" +version = "0.203.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42b4ee6dbac20aa287a416f8905ed64b95cb484063c2af6be4eb232382c7fcb6" +checksum = "318310b221f1da7ff04b959b79001fcfd56ad329e9a44fe3c86b27378b5785eb" dependencies = [ "async-compression", "async-trait", - "base64 0.21.7", + "base64 0.22.1", "brotli 6.0.0", "bytes", "cache_control", "deno_core", + "deno_error", "deno_net", + "deno_telemetry", "deno_websocket", "flate2", "http 0.2.12", "http 1.2.0", "httparse", "hyper 0.14.32", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", - "itertools 0.10.5", - "memmem", + "itertools 0.14.0", + "log", + "memchr", "mime", "once_cell", "percent-encoding", @@ -2223,19 +2362,20 @@ dependencies = [ "scopeguard", "serde", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tokio-util", ] [[package]] name = "deno_io" -version = "0.89.0" +version = "0.115.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc19195805a6b256d5ffe697c81ac79f8acd22246616fe880d6c9ec2dacf9bb4" +checksum = "a4ab5157e8769632476045608317617a96cef09d0fde6b123262bbc038f2f5c1" dependencies = [ "async-trait", "deno_core", + "deno_error", "filetime", "fs3", "libc", @@ -2244,26 +2384,28 @@ dependencies = [ "os_pipe", "parking_lot 0.12.3", "pin-project", - "rand", + "rand 0.8.5", "tokio", "uuid", "winapi", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "deno_kv" -version = "0.87.0" +version = "0.113.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a25347cd7ae561d0b05c24eebb3047e85a3af3f398675d5a9894fd167f2714f" +checksum = "bcee10c3519a52278a6650300c01e6815ddb63167438417ecd5bf1ff9521d413" dependencies = [ "anyhow", "async-trait", - "base64 0.21.7", + "base64 0.22.1", "boxed_error", "bytes", "chrono", "deno_core", + "deno_error", + "deno_features", "deno_fetch", "deno_path_util", "deno_permissions", @@ -2271,25 +2413,36 @@ dependencies = [ "denokv_proto", "denokv_remote", "denokv_sqlite", - "faster-hex", + "faster-hex 0.10.0", "http 1.2.0", "http-body-util", "log", "num-bigint", - "prost", - "prost-build", - "rand", + "rand 0.8.5", "rusqlite", "serde", - "thiserror 1.0.69", + "thiserror 2.0.12", "url", ] [[package]] -name = "deno_media_type" -version = "0.2.2" +name = "deno_lockfile" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa135b8a9febc9a51c16258e294e268a1276750780d69e46edb31cced2826e4" +checksum = "a7a03d93aa789e2652a644e3e71638b4e21faafad5f7392dc8f126589d158695" +dependencies = [ + "async-trait", + "deno_semver", + "serde", + "serde_json", + "thiserror 2.0.12", +] + +[[package]] +name = "deno_media_type" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d9080fcfcea53bcd6eea1916217bd5611c896f3a0db4c001a859722a1258a47" dependencies = [ "data-url", "serde", @@ -2298,18 +2451,20 @@ dependencies = [ [[package]] name = "deno_napi" -version = "0.110.0" +version = "0.136.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea57b67488969f82594cb008fed1bd99830e6db042e31ee9878933d8c76be41c" +checksum = "3140183d12f71f72698a42c9e2a8236ed6a49442352cc15e2801aed8b4253c32" dependencies = [ "deno_core", + "deno_error", "deno_permissions", + "denort_helper", "libc", "libloading 0.7.4", "log", "napi_sym", - "thiserror 1.0.69", - "windows-sys 0.52.0", + "thiserror 2.0.12", + "windows-sys 0.59.0", ] [[package]] @@ -2327,76 +2482,81 @@ dependencies = [ [[package]] name = "deno_net" -version = "0.171.0" +version = "0.197.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b3a51f7b4d5d64d17a7bc6f7495498f20d809930979d21a059d75e850cdea6" +checksum = "6291f993f09b403f873c7426fbf47a2c06a56cbc7c226fe02b03775be16ce805" dependencies = [ "deno_core", + "deno_error", + "deno_features", "deno_permissions", "deno_tls", "hickory-proto", "hickory-resolver", "pin-project", + "quinn", "rustls-tokio-stream", "serde", + "sha2", "socket2", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", + "tokio-vsock", + "url", + "web-transport-proto", ] [[package]] name = "deno_node" -version = "0.116.0" +version = "0.143.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd0d1a757f75224e84ce8a553c2465e4a352fba4b7551ec15809d8a119847e7" +checksum = "1db9fe4769c2d64dd0f43e8c167606f06d5de0aded7714b89f2f378b11608089" dependencies = [ "aead-gcm-stream", "aes", - "async-trait", - "base64 0.21.7", + "base64 0.22.1", "blake2", "boxed_error", "brotli 6.0.0", "bytes", "cbc", "const-oid", + "ctr", "data-encoding", "deno_core", + "deno_error", "deno_fetch", "deno_fs", "deno_io", - "deno_media_type", "deno_net", "deno_package_json", "deno_path_util", "deno_permissions", + "deno_process", "deno_whoami", "der", "digest", + "dotenvy", "dsa", "ecb", "ecdsa", "ed25519-dalek", "elliptic-curve", - "errno 0.2.8", - "faster-hex", + "errno", + "faster-hex 0.10.0", "h2 0.4.7", "hkdf", - "home", "http 1.2.0", "http-body-util", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", "idna", - "indexmap 2.7.0", "ipnetwork", "k256", - "lazy-regex", "libc", "libz-sys", "md-5", "md4", - "memchr", "node_resolver", "num-bigint", "num-bigint-dig", @@ -2406,15 +2566,13 @@ dependencies = [ "p224", "p256", "p384", - "path-clean", "pbkdf2", - "pin-project-lite", "pkcs8", - "rand", - "regex", + "rand 0.8.5", "ring", "ripemd", "rsa", + "rusqlite", "scrypt", "sec1", "serde", @@ -2422,92 +2580,208 @@ dependencies = [ "sha2", "sha3", "signature", - "simd-json", "sm3", "spki", - "stable_deref_trait", - "thiserror 1.0.69", + "sys_traits", + "thiserror 2.0.12", "tokio", "tokio-eld", + "tower-service", "url", "webpki-root-certs", "winapi", - "windows-sys 0.52.0", + "windows-sys 0.59.0", "x25519-dalek", "x509-parser", "yoke", ] [[package]] -name = "deno_ops" -version = "0.197.0" +name = "deno_npm" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a8825d92301cf445727c43f17fee2a20fcdf4370004339965156ae7c56c97e" +checksum = "c9cf5aab0fbd2e68c022fef8981a92c4e4b0fcec341c08af1ebb3651f03cd86b" dependencies = [ + "async-trait", + "capacity_builder", + "deno_error", + "deno_lockfile", + "deno_semver", + "futures", + "indexmap 2.9.0", + "log", + "monch", + "serde", + "serde_json", + "thiserror 2.0.12", + "url", +] + +[[package]] +name = "deno_ops" +version = "0.223.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5adc7f0795c7547f1b560a07aaea484e8f9cd035318348c6bfd084e0c42dce8" +dependencies = [ + "indexmap 2.9.0", "proc-macro-rules", "proc-macro2", "quote", "stringcase", - "strum", - "strum_macros", + "strum 0.27.1", + "strum_macros 0.27.1", "syn 2.0.101", - "thiserror 1.0.69", + "thiserror 2.0.12", +] + +[[package]] +name = "deno_os" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572571939a1c267dc48b20d128e22e2917f778a9657b9e600e3358936322be08" +dependencies = [ + "deno_core", + "deno_error", + "deno_path_util", + "deno_permissions", + "deno_telemetry", + "libc", + "netif", + "ntapi", + "once_cell", + "serde", + "signal-hook", + "signal-hook-registry", + "thiserror 2.0.12", + "tokio", + "winapi", ] [[package]] name = "deno_package_json" -version = "0.1.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbc4c4d3eb0960b58e8f43f9fc2d3f620fcac9a03cd85203e08db5b04e83c1f" +checksum = "236bc2d6d6c06b68cbde960542e13501cf833c975f221a012da619f714c57123" dependencies = [ + "boxed_error", + "deno_error", + "deno_path_util", "deno_semver", - "indexmap 2.7.0", + "indexmap 2.9.0", "serde", "serde_json", - "thiserror 1.0.69", + "sys_traits", + "thiserror 2.0.12", "url", ] [[package]] name = "deno_path_util" -version = "0.2.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff25f6e08e7a0214bbacdd6f7195c7f1ebcd850c87a624e4ff06326b68b42d99" +checksum = "c238a664a0a6f1ce0ff2b73c6854811526d00f442a12f878cb8555b23fe13aa3" dependencies = [ + "deno_error", "percent-encoding", - "thiserror 1.0.69", + "sys_traits", + "thiserror 2.0.12", "url", ] [[package]] name = "deno_permissions" -version = "0.39.0" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e822f98185ab3ddf06104b2407681e0008af52361af32f1cd171b7eda5aa59" +checksum = "501f5bb2f44b977eb682c42909df35b980edf5144d3b5f00796e96a67df0055c" dependencies = [ + "capacity_builder", "deno_core", + "deno_error", "deno_path_util", - "deno_terminal 0.2.0", + "deno_terminal", "fqdn", "libc", "log", "once_cell", "percent-encoding", "serde", - "thiserror 1.0.69", - "which 4.4.2", + "thiserror 2.0.12", + "which 6.0.3", "winapi", ] [[package]] -name = "deno_runtime" -version = "0.188.0" +name = "deno_process" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516ed4f796ab0f5dc092b5592ed6159c759f4f3a94f4a23455fecc94edc51dd1" +checksum = "cce5d661da16ef5d52a218231037344f43fcbe7c02cd7243f9296d3ae4127351" dependencies = [ + "deno_core", + "deno_error", + "deno_fs", + "deno_io", + "deno_os", + "deno_path_util", + "deno_permissions", + "libc", + "log", + "memchr", + "nix 0.27.1", + "pin-project-lite", + "rand 0.8.5", + "serde", + "simd-json", + "tempfile", + "thiserror 2.0.12", + "tokio", + "which 6.0.3", + "winapi", + "windows-sys 0.59.0", +] + +[[package]] +name = "deno_resolver" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80ef70c20f52ff0461f5558738afed7fd360b664bc641788c8a48a76562660f4" +dependencies = [ + "anyhow", + "async-once-cell", "async-trait", + "base32", + "boxed_error", + "dashmap", + "deno_cache_dir", + "deno_config", + "deno_error", + "deno_media_type", + "deno_npm", + "deno_package_json", + "deno_path_util", + "deno_semver", + "deno_terminal", + "deno_unsync", + "futures", + "import_map", + "indexmap 2.9.0", + "log", + "node_resolver", + "once_cell", + "parking_lot 0.12.3", + "serde", + "serde_json", + "sys_traits", + "thiserror 2.0.12", + "url", +] + +[[package]] +name = "deno_runtime" +version = "0.213.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee2725c7610460a10b771ea5fecece28bf9e56f7c87b44d9fda4992490afeb5" +dependencies = [ "color-print", - "deno_ast", "deno_broadcast_channel", "deno_cache", "deno_canvas", @@ -2515,6 +2789,8 @@ dependencies = [ "deno_core", "deno_cron", "deno_crypto", + "deno_error", + "deno_features", "deno_fetch", "deno_ffi", "deno_fs", @@ -2524,9 +2800,13 @@ dependencies = [ "deno_napi", "deno_net", "deno_node", + "deno_os", "deno_path_util", "deno_permissions", - "deno_terminal 0.2.0", + "deno_process", + "deno_resolver", + "deno_telemetry", + "deno_terminal", "deno_tls", "deno_url", "deno_web", @@ -2534,75 +2814,81 @@ dependencies = [ "deno_webidl", "deno_websocket", "deno_webstorage", - "dlopen2 0.6.1", "encoding_rs", "fastwebsockets", - "flate2", "http 1.2.0", "http-body-util", - "hyper 0.14.32", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", "libc", "log", - "netif", "nix 0.27.1", "node_resolver", "notify", "ntapi", "once_cell", + "rustyline", + "same-file", + "serde", + "sys_traits", + "thiserror 2.0.12", + "tokio", + "tokio-metrics", + "twox-hash", + "uuid", + "winapi", + "windows-sys 0.59.0", +] + +[[package]] +name = "deno_semver" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4775271f9b5602482698f76d24ea9ed8ba27af7f587a7e9a876916300c542435" +dependencies = [ + "capacity_builder", + "deno_error", + "ecow", + "hipstr", + "monch", + "once_cell", + "serde", + "thiserror 2.0.12", + "url", +] + +[[package]] +name = "deno_telemetry" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c178ae71033b1086b35b4a256a31e8c331623956a79f2177d6b35d6a656cce88" +dependencies = [ + "async-trait", + "deno_core", + "deno_error", + "deno_tls", + "http-body-util", + "hyper 1.6.0", + "hyper-rustls", + "hyper-util", + "log", + "once_cell", "opentelemetry", "opentelemetry-http", "opentelemetry-otlp", "opentelemetry-semantic-conventions", "opentelemetry_sdk", - "percent-encoding", "pin-project", - "regex", - "rustyline", - "same-file", "serde", - "signal-hook", - "signal-hook-registry", - "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", - "tokio-metrics", - "twox-hash", - "uuid", - "which 4.4.2", - "winapi", - "windows-sys 0.52.0", -] - -[[package]] -name = "deno_semver" -version = "0.5.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c957c6a57c38b7dde2315df0da0ec228911e56a74f185b108a488d0401841a67" -dependencies = [ - "monch", - "once_cell", - "serde", - "thiserror 1.0.69", - "url", ] [[package]] name = "deno_terminal" -version = "0.1.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6337d4e7f375f8b986409a76fbeecfa4bd8a1343e63355729ae4befa058eaf" -dependencies = [ - "once_cell", - "termcolor", -] - -[[package]] -name = "deno_terminal" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daef12499e89ee99e51ad6000a91f600d3937fb028ad4918af76810c5bc9e0d5" +checksum = "23f71c27009e0141dedd315f1dfa3ebb0a6ca4acce7c080fac576ea415a465f6" dependencies = [ "once_cell", "termcolor", @@ -2610,18 +2896,19 @@ dependencies = [ [[package]] name = "deno_tls" -version = "0.166.0" +version = "0.192.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688175eed35e7b3053ec114227894ef24786855405d8844058a48bffa997d85a" +checksum = "423a3b90429be302bbcccbea6ca6a539c0d2f97a54e132458c41004ea2ad20d1" dependencies = [ "deno_core", + "deno_error", "deno_native_certs", "rustls 0.23.20", "rustls-pemfile", "rustls-tokio-stream", "rustls-webpki", "serde", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "webpki-roots", ] @@ -2639,67 +2926,73 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.179.0" +version = "0.205.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9a108794e505f2b07665e19ff336c1bcba6adcf7182c90c1d3a6c741d7fcd0" +checksum = "af2813696e113b21c288558151c86a0f60d1afda3458bc76e5d29173fe288c22" dependencies = [ "deno_core", - "thiserror 1.0.69", + "deno_error", "urlpattern", ] [[package]] name = "deno_web" -version = "0.210.0" +version = "0.236.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7679087bcc41f7ae3385f8c12d43bc81cfc54cb9b1ef73983d20f5e39fa4e0da" +checksum = "8b0885564bfade3284b26a29cb32a67ba7b75fe329b7f12bc0e26815acf7ae1c" dependencies = [ "async-trait", "base64-simd 0.8.0", "bytes", "deno_core", + "deno_error", "deno_permissions", "encoding_rs", "flate2", "futures", "serde", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "uuid", ] [[package]] name = "deno_webgpu" -version = "0.146.0" +version = "0.172.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f78b73638be1552b31778e42267f4fb47e902f7b261bdb0f951ba2b1d6bfab" +checksum = "a2b7cc8f70ea5508386468108310c90d7baf251b90dfff76f22474eac7d0ed2f" dependencies = [ "deno_core", + "deno_error", + "deno_unsync", + "indexmap 2.9.0", "raw-window-handle", "serde", - "thiserror 1.0.69", + "serde_json", + "thiserror 2.0.12", "tokio", - "wgpu-core 0.21.1", - "wgpu-types 0.20.0", + "wgpu-core 24.0.5", + "wgpu-types 24.0.0", ] [[package]] name = "deno_webidl" -version = "0.179.0" +version = "0.205.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b55d845e3d64f8de7eff67aaa4b6fe1b23bbc2efe967c984f8c64c8dd85fad4" +checksum = "7e4f3a7a4672a071d25e93b5598f4a8a4bfc20f2a8d6900a7a6a7d02264bc6f2" dependencies = [ "deno_core", ] [[package]] name = "deno_websocket" -version = "0.184.0" +version = "0.210.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00407052c6524828f2708557c47059ba9b87874758416c66f47f5102ac68422" +checksum = "70452e61dd4e7c157aedb14e0f9b2cd3199f76a53138793afc1ebdf82601c12b" dependencies = [ "bytes", "deno_core", + "deno_error", "deno_net", "deno_permissions", "deno_tls", @@ -2707,25 +3000,25 @@ dependencies = [ "h2 0.4.7", "http 1.2.0", "http-body-util", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", "once_cell", "rustls-tokio-stream", "serde", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", ] [[package]] name = "deno_webstorage" -version = "0.174.0" +version = "0.200.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecaabbb1580d21811642f11cc12fe8599684efeb9398eaa998a3db8811e8edc" +checksum = "c120f49410ced3722ef030c817fb9a10f1cd0c9b12b438de91cc466231780f83" dependencies = [ "deno_core", - "deno_web", + "deno_error", "rusqlite", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] @@ -2740,13 +3033,13 @@ dependencies = [ [[package]] name = "denokv_proto" -version = "0.8.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ba1f99ed11a9c11e868a8521b1f71a7e1aba785d7f42ea9ecbdc01146c89ec" +checksum = "fdc7c5c829ce15275d0898c94eecc243e2a47269a3f8ec5a1da45fe268a90886" dependencies = [ - "anyhow", "async-trait", "chrono", + "deno_error", "futures", "num-bigint", "prost", @@ -2756,23 +3049,24 @@ dependencies = [ [[package]] name = "denokv_remote" -version = "0.8.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08ed833073189e8f6d03155fe3b05a024e75e29d8a28a4c2e9ec3b5c925e727b" +checksum = "ecd57015ff7b5d51cd7a61b83baec8e38367631cd13dc77140412fe5143e15fb" dependencies = [ - "anyhow", "async-stream", "async-trait", "bytes", "chrono", + "deno_error", "denokv_proto", "futures", "http 1.2.0", "log", "prost", - "rand", + "rand 0.8.5", "serde", "serde_json", + "thiserror 2.0.12", "tokio", "tokio-util", "url", @@ -2781,29 +3075,42 @@ dependencies = [ [[package]] name = "denokv_sqlite" -version = "0.8.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b790f01d1302d53a0c3cbd27de88a06b3abd64ec8ab8673924e490541c7c713" +checksum = "01024c5ad6ce7838d27dc35cfcc0877eee57e07a25126ccaac8eb2b61a0cf04f" dependencies = [ - "anyhow", "async-stream", "async-trait", "chrono", + "deno_error", "denokv_proto", "futures", "hex", "log", "num-bigint", - "rand", + "rand 0.8.5", "rusqlite", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tokio-stream", "uuid", "v8_valueserializer", ] +[[package]] +name = "denort_helper" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94ac464246974fec810ff9fc05803ec7f1467ae2e7e76a76bd9bec6ce282b1e9" +dependencies = [ + "deno_error", + "deno_path_util", + "sys_traits", + "thiserror 2.0.12", + "twox-hash", +] + [[package]] name = "der" version = "0.7.9" @@ -2973,7 +3280,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "objc2 0.6.1", ] @@ -3080,28 +3387,13 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" -[[package]] -name = "dprint-swc-ext" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba28c12892aadb751c2ba7001d8460faee4748a04b4edc51c7121cc67ee03db" -dependencies = [ - "num-bigint", - "rustc-hash 1.1.0", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_parser", - "text_lines", -] - [[package]] name = "drm" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "bytemuck", "drm-ffi", "drm-fourcc", @@ -3150,38 +3442,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clone" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" -[[package]] -name = "dynasm" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" -dependencies = [ - "bitflags 1.3.2", - "byteorder", - "lazy_static", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "dynasmrt" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" -dependencies = [ - "byteorder", - "dynasm", - "memmap2 0.5.10", -] - [[package]] name = "ecb" version = "0.1.2" @@ -3205,6 +3477,15 @@ dependencies = [ "spki", ] +[[package]] +name = "ecow" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b92b481eb5d59fd8e80e92ff11d057d1ca8d144b2cd8c66cc8d5bd177a3c0dc5" +dependencies = [ + "serde", +] + [[package]] name = "ed25519" version = "2.2.3" @@ -3223,7 +3504,7 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core", + "rand_core 0.6.4", "serde", "sha2", "signature", @@ -3248,9 +3529,6 @@ name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" -dependencies = [ - "serde", -] [[package]] name = "elliptic-curve" @@ -3268,7 +3546,7 @@ dependencies = [ "hkdf", "pem-rfc7468", "pkcs8", - "rand_core", + "rand_core 0.6.4", "sec1", "serde_json", "serdect", @@ -3342,9 +3620,9 @@ checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -3416,17 +3694,6 @@ dependencies = [ "typeid", ] -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - [[package]] name = "errno" version = "0.3.10" @@ -3437,16 +3704,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "error-code" version = "3.3.1" @@ -3469,17 +3726,6 @@ dependencies = [ "svg_fmt", ] -[[package]] -name = "etcetera" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" -dependencies = [ - "cfg-if", - "home", - "windows-sys 0.48.0", -] - [[package]] name = "euclid" version = "0.22.11" @@ -3519,7 +3765,7 @@ dependencies = [ "bit_field", "half", "lebe", - "miniz_oxide 0.8.2", + "miniz_oxide 0.8.8", "rayon-core", "smallvec", "zune-inflate", @@ -3558,6 +3804,16 @@ dependencies = [ "serde", ] +[[package]] +name = "faster-hex" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7223ae2d2f179b803433d9c830478527e92b8117eab39460edae7f1614d9fb73" +dependencies = [ + "heapless", + "serde", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -3573,10 +3829,10 @@ dependencies = [ "base64 0.21.7", "bytes", "http-body-util", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", "pin-project", - "rand", + "rand 0.8.5", "sha1", "simdutf8", "thiserror 1.0.69", @@ -3610,7 +3866,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -3661,7 +3917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", - "miniz_oxide 0.8.2", + "miniz_oxide 0.8.8", ] [[package]] @@ -3679,17 +3935,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - [[package]] name = "fnv" version = "1.0.7" @@ -3828,17 +4073,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "from_variant" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" -dependencies = [ - "proc-macro2", - "swc_macros_common", - "syn 2.0.101", -] - [[package]] name = "fs3" version = "0.5.0" @@ -3928,17 +4162,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "futures-intrusive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot 0.12.3", -] - [[package]] name = "futures-io" version = "0.3.31" @@ -4062,13 +4285,12 @@ dependencies = [ "gauntlet-utils", "gauntlet-utils-macros", "gix-url", - "indexmap 2.7.0", + "indexmap 2.9.0", "itertools 0.13.0", "libc", "prost", "serde", "serde_json", - "thiserror 2.0.8", "tokio", "tonic", "tonic-build", @@ -4106,7 +4328,7 @@ name = "gauntlet-component-model" version = "0.0.0" dependencies = [ "anyhow", - "indexmap 2.7.0", + "indexmap 2.9.0", "serde", "serde_json", ] @@ -4147,6 +4369,8 @@ dependencies = [ "cacao", "cosmic-protocols", "deno_core", + "deno_error", + "deno_resolver", "deno_runtime", "encoding", "freedesktop-icons", @@ -4158,7 +4382,7 @@ dependencies = [ "gauntlet-utils", "icns", "image 0.25.5", - "indexmap 2.7.0", + "indexmap 2.9.0", "interprocess", "libc", "numbat", @@ -4173,6 +4397,8 @@ dependencies = [ "serde", "smithay-client-toolkit", "sys-locale", + "sys_traits", + "thiserror 2.0.12", "tokio", "tokio-util", "tracing", @@ -4182,7 +4408,7 @@ dependencies = [ "wayland-client", "wayland-protocols-wlr 0.3.5", "which 7.0.1", - "windows", + "windows 0.58.0", "x11rb", ] @@ -4213,6 +4439,7 @@ dependencies = [ "gauntlet-plugin-runtime", "gauntlet-scenario-runner", "gauntlet-utils", + "gauntlet-utils-macros", "git2", "image 0.25.5", "include_dir", @@ -4221,9 +4448,10 @@ dependencies = [ "once_cell", "open", "regex", + "rusqlite", "schemars", "serde", - "sqlx", + "serde_json", "tantivy", "tempfile", "tokio", @@ -4246,7 +4474,7 @@ version = "0.0.0" dependencies = [ "anyhow", "prost", - "thiserror 2.0.8", + "thiserror 2.0.12", "tokio", "tonic", ] @@ -4319,6 +4547,20 @@ dependencies = [ "system-deps", ] +[[package]] +name = "generator" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows 0.61.1", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -4349,7 +4591,21 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", "wasm-bindgen", ] @@ -4378,6 +4634,11 @@ name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +dependencies = [ + "fallible-iterator", + "indexmap 2.9.0", + "stable_deref_trait", +] [[package]] name = "gio" @@ -4417,7 +4678,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "libc", "libgit2-sys", "log", @@ -4443,8 +4704,8 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b5eccc17194ed0e67d49285e4853307e4147e95407f91c1c3e4a13ba9f4e4ce" dependencies = [ - "faster-hex", - "thiserror 2.0.8", + "faster-hex 0.9.0", + "thiserror 2.0.12", ] [[package]] @@ -4457,7 +4718,7 @@ dependencies = [ "gix-trace", "home", "once_cell", - "thiserror 2.0.8", + "thiserror 2.0.12", ] [[package]] @@ -4475,7 +4736,7 @@ dependencies = [ "bstr", "gix-features", "gix-path", - "thiserror 2.0.8", + "thiserror 2.0.12", "url", ] @@ -4502,7 +4763,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "futures-channel", "futures-core", "futures-executor", @@ -4560,22 +4821,23 @@ dependencies = [ "objc2 0.6.1", "objc2-app-kit 0.3.1", "once_cell", - "thiserror 2.0.8", + "thiserror 2.0.12", "windows-sys 0.59.0", "x11rb", "xkeysym", ] [[package]] -name = "glow" -version = "0.13.1" +name = "globset" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -4591,12 +4853,15 @@ dependencies = [ ] [[package]] -name = "glutin_wgl_sys" -version = "0.5.0" +name = "glow" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" dependencies = [ - "gl_generator", + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -4637,7 +4902,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "gpu-alloc-types", ] @@ -4647,7 +4912,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", ] [[package]] @@ -4659,7 +4924,7 @@ dependencies = [ "log", "presser", "thiserror 1.0.69", - "windows", + "windows 0.58.0", ] [[package]] @@ -4668,7 +4933,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "gpu-descriptor-types", "hashbrown 0.15.2", ] @@ -4679,7 +4944,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", ] [[package]] @@ -4689,7 +4954,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -4776,7 +5041,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.7.0", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -4795,7 +5060,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.2.0", - "indexmap 2.7.0", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -4822,6 +5087,15 @@ dependencies = [ "serde", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -4854,11 +5128,11 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -4875,6 +5149,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -4916,10 +5200,11 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "hickory-proto" -version = "0.24.2" +version = "0.25.0-alpha.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" +checksum = "1d00147af6310f4392a31680db52a3ed45a2e0f68eb18e8c3fe5537ecc96d9e2" dependencies = [ + "async-recursion", "async-trait", "cfg-if", "data-encoding", @@ -4930,9 +5215,9 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand", + "rand 0.9.1", "serde", - "thiserror 1.0.69", + "thiserror 2.0.12", "tinyvec", "tokio", "tracing", @@ -4941,26 +5226,37 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.2" +version = "0.25.0-alpha.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" +checksum = "5762f69ebdbd4ddb2e975cd24690bf21fe6b2604039189c26acddbc427f12887" dependencies = [ "cfg-if", "futures-util", "hickory-proto", "ipconfig", - "lru-cache", + "moka", "once_cell", "parking_lot 0.12.3", - "rand", + "rand 0.9.1", "resolv-conf", "serde", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tracing", ] +[[package]] +name = "hipstr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97971ffc85d4c98de12e2608e992a43f5294ebb625fdb045b27c731b64c4c6d6" +dependencies = [ + "serde", + "serde_bytes", + "sptr", +] + [[package]] name = "hkdf" version = "0.12.4" @@ -4999,20 +5295,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "hstr" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dae404c0c5d4e95d4858876ab02eecd6a196bb8caa42050dfa809938833fc412" -dependencies = [ - "hashbrown 0.14.5", - "new_debug_unreachable", - "once_cell", - "phf", - "rustc-hash 1.1.0", - "triomphe", -] - [[package]] name = "htmlescape" version = "0.3.1" @@ -5122,9 +5404,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -5149,7 +5431,7 @@ checksum = "f6884a48c6826ec44f524c7456b163cebe9e55a18d7b5e307cb4f100371cc767" dependencies = [ "futures-util", "http 1.2.0", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", "rustls 0.23.20", "rustls-pki-types", @@ -5164,7 +5446,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 1.5.2", + "hyper 1.6.0", "hyper-util", "pin-project-lite", "tokio", @@ -5173,20 +5455,20 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.2", + "hyper 1.6.0", + "libc", "pin-project-lite", "socket2", "tokio", - "tower 0.4.13", "tower-service", "tracing", ] @@ -5247,7 +5529,7 @@ name = "iced_core" version = "0.13.99" source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "bytes", "dark-light", "glam", @@ -5288,7 +5570,7 @@ name = "iced_graphics" version = "0.13.99" source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "bytemuck", "cosmic-text", "half", @@ -5389,7 +5671,7 @@ name = "iced_wgpu" version = "0.13.99" source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "bytemuck", "futures", "glam", @@ -5598,6 +5880,22 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" +[[package]] +name = "ignore" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.9", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "image" version = "0.24.9" @@ -5667,6 +5965,23 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" +[[package]] +name = "import_map" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1215d4d92511fbbdaea50e750e91f2429598ef817f02b579158e92803b52c00a" +dependencies = [ + "boxed_error", + "deno_error", + "indexmap 2.9.0", + "log", + "percent-encoding", + "serde", + "serde_json", + "thiserror 2.0.12", + "url", +] + [[package]] name = "include_dir" version = "0.7.4" @@ -5699,9 +6014,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -5818,18 +6133,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "is-macro" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "is-wsl" version = "0.4.0" @@ -5846,15 +6149,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -5873,6 +6167,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" @@ -5948,22 +6251,31 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] -name = "junction" -version = "0.2.0" +name = "jsonc-parser" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be39922b087cecaba4e2d5592dedfc8bda5d4a5a1231f143337cca207950b61d" +checksum = "b558af6b49fd918e970471374e7a798b2c9bbcda624a210ffa3901ee5614bc8e" +dependencies = [ + "serde_json", +] + +[[package]] +name = "junction" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72bbdfd737a243da3dfc1f99ee8d6e166480f17ab4ac84d7c34aacd73fc7bd16" dependencies = [ "scopeguard", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -6004,7 +6316,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "serde", "unicode-segmentation", ] @@ -6071,7 +6383,7 @@ name = "layershellev" version = "0.13.99" source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13#de88e8d3be5e7ebef6d8456ea62eed26e503bf92" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "calloop 0.14.2", "calloop-wayland-source 0.4.0", "raw-window-handle", @@ -6119,6 +6431,29 @@ dependencies = [ "spin", ] +[[package]] +name = "lcms2" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680ec3fa42c36e0af9ca02f20a3742a82229c7f1ee0e6754294de46a80be6f74" +dependencies = [ + "bytemuck", + "foreign-types 0.5.0", + "lcms2-sys", +] + +[[package]] +name = "lcms2-sys" +version = "4.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "593265f9a3172180024fb62580ee31348f31be924b19416da174ebb7fb623d2e" +dependencies = [ + "cc", + "dunce", + "libc", + "pkg-config", +] + [[package]] name = "lebe" version = "0.5.2" @@ -6157,15 +6492,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.169" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libffi" -version = "3.2.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce826c243048e3d5cec441799724de52e2d42f820468431fc3fceee2341871e2" +checksum = "4a9434b6fc77375fb624698d5f8c49d7e80b10d59eb1219afda27d1f824d4074" dependencies = [ "libc", "libffi-sys", @@ -6173,8 +6508,9 @@ dependencies = [ [[package]] name = "libffi-sys" -version = "2.3.0" -source = "git+https://github.com/tov/libffi-rs?rev=d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b#d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ead36a2496acfc8edd6cc32352110e9478ac5b9b5f5b9856ebd3d28019addb84" dependencies = [ "cc", ] @@ -6235,17 +6571,18 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "libc", "redox_syscall 0.5.8", ] [[package]] name = "libsqlite3-sys" -version = "0.30.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +checksum = "fbb8270bb4060bd76c6e96f20c52d80620f1d82a3470885694e41e0f81ef6fe7" dependencies = [ + "bindgen", "cc", "pkg-config", "vcpkg", @@ -6277,12 +6614,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -6323,6 +6654,19 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "loop9" version = "0.1.5" @@ -6342,13 +6686,10 @@ dependencies = [ ] [[package]] -name = "lru-cache" +name = "lru-slab" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "lz4_flex" @@ -6455,15 +6796,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - [[package]] name = "memmap2" version = "0.8.0" @@ -6482,12 +6814,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memmem" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" - [[package]] name = "memoffset" version = "0.9.1" @@ -6508,11 +6834,11 @@ dependencies = [ [[package]] name = "metal" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "block", "core-graphics-types 0.1.3", "foreign-types 0.5.0", @@ -6523,11 +6849,11 @@ dependencies = [ [[package]] name = "metal" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "block", "core-graphics-types 0.1.3", "foreign-types 0.5.0", @@ -6569,18 +6895,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -6594,7 +6911,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -6605,10 +6922,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] +[[package]] +name = "moka" +version = "0.12.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" +dependencies = [ + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "loom", + "parking_lot 0.12.3", + "portable-atomic", + "rustc_version 0.4.1", + "smallvec", + "tagptr", + "thiserror 1.0.69", + "uuid", +] + [[package]] name = "monch" version = "0.5.0" @@ -6654,20 +6990,19 @@ checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" [[package]] name = "naga" -version = "0.20.0" +version = "23.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" +checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f" dependencies = [ "arrayvec", - "bit-set 0.5.3", - "bitflags 2.6.0", + "bit-set", + "bitflags 2.9.1", + "cfg_aliases 0.1.1", "codespan-reporting", "hexf-parse", - "indexmap 2.7.0", + "indexmap 2.9.0", "log", - "num-traits", "rustc-hash 1.1.0", - "serde", "spirv", "termcolor", "thiserror 1.0.69", @@ -6676,30 +7011,32 @@ dependencies = [ [[package]] name = "naga" -version = "23.1.0" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f" +checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e" dependencies = [ "arrayvec", - "bit-set 0.8.0", - "bitflags 2.6.0", - "cfg_aliases 0.1.1", + "bit-set", + "bitflags 2.9.1", + "cfg_aliases 0.2.1", "codespan-reporting", "hexf-parse", - "indexmap 2.7.0", + "indexmap 2.9.0", "log", "rustc-hash 1.1.0", + "serde", "spirv", + "strum 0.26.3", "termcolor", - "thiserror 1.0.69", + "thiserror 2.0.12", "unicode-xid", ] [[package]] name = "napi_sym" -version = "0.109.0" +version = "0.135.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b3ee1b2d30885de3ee82429b5aebe6f22b3eae5cb290cd8d6537a62212812b" +checksum = "f0166fe95fb1a7a76615859d61ed40b8ce1931189cfdf1df81cdcfd8ff38e7c0" dependencies = [ "quote", "serde", @@ -6713,7 +7050,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "jni-sys", "log", "ndk-sys 0.6.0+11769913", @@ -6777,7 +7114,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cfg-if", "libc", ] @@ -6788,7 +7125,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cfg-if", "cfg_aliases 0.1.1", "libc", @@ -6800,7 +7137,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -6809,24 +7146,29 @@ dependencies = [ [[package]] name = "node_resolver" -version = "0.18.0" +version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e999e1cdbb49cdfa3f63ddd061c57205aa5f7be8f43bdbc4081c0f60d24d7d" +checksum = "49baa3423d3ea08c85d8047652a0ec94400a42dbc2118c588b55f19708a99019" dependencies = [ "anyhow", "async-trait", "boxed_error", + "dashmap", + "deno_config", + "deno_error", "deno_media_type", "deno_package_json", "deno_path_util", + "deno_semver", "futures", "lazy-regex", "once_cell", "path-clean", "regex", + "serde", "serde_json", - "thiserror 1.0.69", - "tokio", + "sys_traits", + "thiserror 2.0.12", "url", ] @@ -6862,7 +7204,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "crossbeam-channel", "filetime", "fsevent-sys", @@ -6902,8 +7244,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", - "rand", - "serde", + "rand 0.8.5", ] [[package]] @@ -6918,7 +7259,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand", + "rand 0.8.5", "serde", "smallvec", "zeroize", @@ -7040,7 +7381,7 @@ checksum = "5124c7a716bd197d4ad501237fa890771f69f38b34eb87f4514fdebf0cdcaf5b" dependencies = [ "codespan-reporting", "heck 0.4.1", - "indexmap 2.7.0", + "indexmap 2.9.0", "itertools 0.12.1", "jiff", "libc", @@ -7052,7 +7393,7 @@ dependencies = [ "numbat-exchange-rates", "plotly", "pretty_dtoa", - "rand", + "rand 0.8.5", "rust-embed", "strfmt", "strsim", @@ -7112,7 +7453,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "block2", "libc", "objc2 0.5.2", @@ -7128,7 +7469,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "objc2 0.6.1", "objc2-foundation 0.3.1", ] @@ -7139,7 +7480,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-core-location", @@ -7163,7 +7504,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -7175,7 +7516,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "dispatch2", "objc2 0.6.1", ] @@ -7216,7 +7557,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "block2", "dispatch", "libc", @@ -7229,7 +7570,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "objc2 0.6.1", "objc2-core-foundation", ] @@ -7252,7 +7593,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -7264,7 +7605,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -7287,7 +7628,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-cloud-kit", @@ -7319,7 +7660,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "block2", "objc2 0.5.2", "objc2-core-location", @@ -7490,9 +7831,11 @@ dependencies = [ "glob", "opentelemetry", "percent-encoding", - "rand", + "rand 0.8.5", "serde_json", "thiserror 1.0.69", + "tokio", + "tokio-stream", "tracing", ] @@ -7520,6 +7863,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-multimap" version = "0.4.3" @@ -7563,12 +7915,12 @@ dependencies = [ [[package]] name = "os_pipe" -version = "1.1.5" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7653,7 +8005,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "rand_core", + "rand_core 0.6.4", "sha2", ] @@ -7767,7 +8119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -7821,7 +8173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.7.0", + "indexmap 2.9.0", ] [[package]] @@ -7841,7 +8193,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -7949,7 +8301,7 @@ checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", "pkcs5", - "rand_core", + "rand_core 0.6.4", "spki", ] @@ -7966,7 +8318,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64 0.22.1", - "indexmap 2.7.0", + "indexmap 2.9.0", "quick-xml 0.32.0", "serde", "time", @@ -7982,7 +8334,7 @@ dependencies = [ "erased-serde", "once_cell", "plotly_derive", - "rand", + "rand 0.8.5", "rinja", "serde", "serde_json", @@ -8024,7 +8376,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.8.2", + "miniz_oxide 0.8.8", ] [[package]] @@ -8054,6 +8406,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + [[package]] name = "powerfmt" version = "0.2.0" @@ -8191,9 +8549,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -8269,35 +8627,6 @@ dependencies = [ "prost", ] -[[package]] -name = "psm" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" -dependencies = [ - "cc", -] - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "qoi" version = "0.4.1" @@ -8346,6 +8675,61 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +dependencies = [ + "bytes", + "cfg_aliases 0.2.1", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.0", + "rustls 0.23.20", + "socket2", + "thiserror 2.0.12", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.1", + "ring", + "rustc-hash 2.1.0", + "rustls 0.23.20", + "rustls-pki-types", + "slab", + "thiserror 2.0.12", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" +dependencies = [ + "cfg_aliases 0.2.1", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.40" @@ -8355,6 +8739,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radium" version = "0.7.0" @@ -8378,8 +8768,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -8389,7 +8789,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -8398,7 +8808,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", ] [[package]] @@ -8408,7 +8827,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", ] [[package]] @@ -8449,8 +8868,8 @@ dependencies = [ "once_cell", "paste", "profiling", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "simd_helpers", "system-deps", "thiserror 1.0.69", @@ -8539,7 +8958,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", ] [[package]] @@ -8548,7 +8967,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 1.0.69", ] @@ -8573,6 +8992,20 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "regalloc2" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc06e6b318142614e4a48bc725abbf08ff166694835c43c9dae5a9009704639a" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown 0.15.2", + "log", + "rustc-hash 2.1.0", + "smallvec", +] + [[package]] name = "regex" version = "1.11.1" @@ -8684,15 +9117,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -8756,7 +9188,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64 0.21.7", - "bitflags 2.6.0", + "bitflags 2.9.1", "serde", "serde_derive", ] @@ -8780,7 +9212,7 @@ dependencies = [ "num-traits", "pkcs1", "pkcs8", - "rand_core", + "rand_core 0.6.4", "signature", "spki", "subtle", @@ -8789,15 +9221,16 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.32.1" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +checksum = "37e34486da88d8e051c7c0e23c3f15fd806ea8546260aa2fec247e97242ec143" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "fallible-iterator", "fallible-streaming-iterator", "hashlink", "libsqlite3-sys", + "serde_json", "smallvec", ] @@ -8917,8 +9350,8 @@ version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ - "bitflags 2.6.0", - "errno 0.3.10", + "bitflags 2.9.1", + "errno", "libc", "linux-raw-sys 0.4.14", "windows-sys 0.59.0", @@ -8980,12 +9413,15 @@ name = "rustls-pki-types" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +dependencies = [ + "web-time", +] [[package]] name = "rustls-tokio-stream" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22557157d7395bc30727745b365d923f1ecc230c4c80b176545f3f4f08c46e33" +checksum = "faa7dc7c991d9164e55bbf1558029eb5b84d32cc4d61a7df5b8641b2deedc4b3" dependencies = [ "futures", "rustls 0.23.20", @@ -9016,7 +9452,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "bytemuck", "libm", "smallvec", @@ -9033,7 +9469,7 @@ version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cfg-if", "clipboard-win", "fd-lock", @@ -9055,12 +9491,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "ryu-js" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" - [[package]] name = "ryu_floating_decimal" version = "0.1.0" @@ -9186,7 +9616,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -9235,9 +9665,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.216" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -9248,7 +9678,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" dependencies = [ - "ordered-float", + "ordered-float 2.10.1", "serde", ] @@ -9263,9 +9693,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -9289,7 +9719,7 @@ version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ - "indexmap 2.7.0", + "indexmap 2.9.0", "itoa", "memchr", "ryu", @@ -9316,28 +9746,17 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_v8" -version = "0.230.0" +version = "0.256.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a783242d2af51d6955cc04bf2b64adb643ab588b61e9573c908a69dabf8c2f" +checksum = "69d69b4e574a9ec6bd0222463e50cf8531986d9c657543888e029d54d909b283" dependencies = [ + "deno_error", "num-bigint", "serde", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.12", "v8", ] @@ -9351,7 +9770,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.7.0", + "indexmap 2.9.0", "serde", "serde_derive", "serde_json", @@ -9463,7 +9882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -9487,7 +9906,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2bcf6c6e164e81bc7a5d49fc6988b3d515d9e8c07457d7b74ffb9324b9cd40" dependencies = [ - "getrandom", + "getrandom 0.2.15", "halfbrown", "ref-cast", "serde", @@ -9580,23 +9999,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -dependencies = [ - "serde", -] - -[[package]] -name = "smartstring" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" -dependencies = [ - "autocfg", - "static_assertions", - "version_check", -] +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "smithay-client-toolkit" @@ -9604,7 +10009,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "bytemuck", "calloop 0.13.0", "calloop-wayland-source 0.3.0", @@ -9648,9 +10053,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -9688,25 +10093,6 @@ dependencies = [ "x11rb", ] -[[package]] -name = "sourcemap" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" -dependencies = [ - "base64-simd 0.7.0", - "bitvec", - "data-encoding", - "debugid", - "if_chain", - "rustc-hash 1.1.0", - "rustc_version 0.2.3", - "serde", - "serde_json", - "unicode-id-start", - "url", -] - [[package]] name = "sourcemap" version = "9.1.2" @@ -9731,9 +10117,6 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] [[package]] name = "spirv" @@ -9741,7 +10124,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", ] [[package]] @@ -9755,208 +10138,10 @@ dependencies = [ ] [[package]] -name = "sqlformat" -version = "0.2.6" +name = "sptr" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" -dependencies = [ - "nom 7.1.3", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" -dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", -] - -[[package]] -name = "sqlx-core" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" -dependencies = [ - "atoi", - "byteorder", - "bytes", - "crc", - "crossbeam-queue", - "either", - "event-listener", - "futures-channel", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashbrown 0.14.5", - "hashlink", - "hex", - "indexmap 2.7.0", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlformat", - "thiserror 1.0.69", - "tokio", - "tokio-stream", - "tracing", - "url", -] - -[[package]] -name = "sqlx-macros" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" -dependencies = [ - "proc-macro2", - "quote", - "sqlx-core", - "sqlx-macros-core", - "syn 2.0.101", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" -dependencies = [ - "dotenvy", - "either", - "heck 0.5.0", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2", - "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", - "syn 2.0.101", - "tempfile", - "tokio", - "url", -] - -[[package]] -name = "sqlx-mysql" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" -dependencies = [ - "atoi", - "base64 0.22.1", - "bitflags 2.6.0", - "byteorder", - "bytes", - "crc", - "digest", - "dotenvy", - "either", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "generic-array", - "hex", - "hkdf", - "hmac", - "itoa", - "log", - "md-5", - "memchr", - "once_cell", - "percent-encoding", - "rand", - "rsa", - "serde", - "sha1", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror 1.0.69", - "tracing", - "whoami", -] - -[[package]] -name = "sqlx-postgres" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" -dependencies = [ - "atoi", - "base64 0.22.1", - "bitflags 2.6.0", - "byteorder", - "crc", - "dotenvy", - "etcetera", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "hex", - "hkdf", - "hmac", - "home", - "itoa", - "log", - "md-5", - "memchr", - "once_cell", - "rand", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror 1.0.69", - "tracing", - "whoami", -] - -[[package]] -name = "sqlx-sqlite" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" -dependencies = [ - "atoi", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "serde_urlencoded", - "sqlx-core", - "tracing", - "url", -] +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" [[package]] name = "stable_deref_trait" @@ -9964,19 +10149,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "stacker" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.59.0", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -9998,34 +10170,11 @@ dependencies = [ "float-cmp 0.9.0", ] -[[package]] -name = "string_enum" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - [[package]] name = "stringcase" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04028eeb851ed08af6aba5caa29f2d59a13ed168cee4d6bd753aeefcf1d636b0" - -[[package]] -name = "stringprep" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", - "unicode-properties", -] +checksum = "72abeda133c49d7bddece6c154728f83eec8172380c80ab7096da9487e20d27c" [[package]] name = "strsim" @@ -10035,20 +10184,42 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.25.0" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros", + "strum_macros 0.26.4", +] + +[[package]] +name = "strum" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +dependencies = [ + "strum_macros 0.27.1", ] [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.101", +] + +[[package]] +name = "strum_macros" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +dependencies = [ + "heck 0.5.0", "proc-macro2", "quote", "rustversion", @@ -10088,373 +10259,6 @@ dependencies = [ "zeno", ] -[[package]] -name = "swc_allocator" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76aa0eb65c0f39f9b6d82a7e5192c30f7ac9a78f084a21f270de1d8c600ca388" -dependencies = [ - "bumpalo", - "hashbrown 0.14.5", - "ptr_meta", - "rustc-hash 1.1.0", - "triomphe", -] - -[[package]] -name = "swc_atoms" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6567e4e67485b3e7662b486f1565bdae54bd5b9d6b16b2ba1a9babb1e42125" -dependencies = [ - "hstr", - "once_cell", - "rustc-hash 1.1.0", - "serde", -] - -[[package]] -name = "swc_cached" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c" -dependencies = [ - "ahash 0.8.11", - "anyhow", - "dashmap", - "once_cell", - "regex", - "serde", -] - -[[package]] -name = "swc_common" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12d0a8eaaf1606c9207077d75828008cb2dfb51b095a766bd2b72ef893576e31" -dependencies = [ - "ast_node", - "better_scoped_tls", - "cfg-if", - "either", - "from_variant", - "new_debug_unreachable", - "num-bigint", - "once_cell", - "rustc-hash 1.1.0", - "serde", - "siphasher 0.3.11", - "sourcemap 9.1.2", - "swc_allocator", - "swc_atoms", - "swc_eq_ignore_macros", - "swc_visit", - "tracing", - "unicode-width", - "url", -] - -[[package]] -name = "swc_config" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" -dependencies = [ - "anyhow", - "indexmap 2.7.0", - "serde", - "serde_json", - "swc_cached", - "swc_config_macro", -] - -[[package]] -name = "swc_config_macro" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c5f56139042c1a95b54f5ca48baa0e0172d369bcc9d3d473dad1de36bae8399" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "swc_ecma_ast" -version = "0.118.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f866d12e4d519052b92a0a86d1ac7ff17570da1272ca0c89b3d6f802cd79df" -dependencies = [ - "bitflags 2.6.0", - "is-macro", - "num-bigint", - "phf", - "scoped-tls", - "serde", - "string_enum", - "swc_atoms", - "swc_common", - "unicode-id-start", -] - -[[package]] -name = "swc_ecma_codegen" -version = "0.155.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7641608ef117cfbef9581a99d02059b522fcca75e5244fa0cbbd8606689c6f" -dependencies = [ - "memchr", - "num-bigint", - "once_cell", - "serde", - "sourcemap 9.1.2", - "swc_allocator", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_codegen_macros", - "tracing", -] - -[[package]] -name = "swc_ecma_codegen_macros" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859fabde36db38634f3fad548dd5e3410c1aebba1b67a3c63e67018fa57a0bca" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "swc_ecma_loader" -version = "0.49.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55fa3d55045b97894bfb04d38aff6d6302ac8a6a38e3bb3dfb0d20475c4974a9" -dependencies = [ - "anyhow", - "pathdiff", - "serde", - "swc_atoms", - "swc_common", - "tracing", -] - -[[package]] -name = "swc_ecma_parser" -version = "0.149.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683dada14722714588b56481399c699378b35b2ba4deb5c4db2fb627a97fb54b" -dependencies = [ - "either", - "new_debug_unreachable", - "num-bigint", - "num-traits", - "phf", - "serde", - "smallvec", - "smartstring", - "stacker", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "tracing", - "typed-arena", -] - -[[package]] -name = "swc_ecma_transforms_base" -version = "0.145.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f21494e75d0bd8ef42010b47cabab9caaed8f2207570e809f6f4eb51a710d1" -dependencies = [ - "better_scoped_tls", - "bitflags 2.6.0", - "indexmap 2.7.0", - "once_cell", - "phf", - "rustc-hash 1.1.0", - "serde", - "smallvec", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_parser", - "swc_ecma_utils", - "swc_ecma_visit", - "tracing", -] - -[[package]] -name = "swc_ecma_transforms_classes" -version = "0.134.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3d884594385bea9405a2e1721151470d9a14d3ceec5dd773c0ca6894791601" -dependencies = [ - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms_macros" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500a1dadad1e0e41e417d633b3d6d5de677c9e0d3159b94ba3348436cdb15aab" -dependencies = [ - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - -[[package]] -name = "swc_ecma_transforms_proposal" -version = "0.179.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79938ff510fc647febd8c6c3ef4143d099fdad87a223680e632623d056dae2dd" -dependencies = [ - "either", - "rustc-hash 1.1.0", - "serde", - "smallvec", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_transforms_classes", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms_react" -version = "0.191.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" -dependencies = [ - "base64 0.21.7", - "dashmap", - "indexmap 2.7.0", - "once_cell", - "serde", - "sha1", - "string_enum", - "swc_allocator", - "swc_atoms", - "swc_common", - "swc_config", - "swc_ecma_ast", - "swc_ecma_parser", - "swc_ecma_transforms_base", - "swc_ecma_transforms_macros", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_transforms_typescript" -version = "0.198.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15455da4768f97186c40523e83600495210c11825d3a44db43383fd81eace88d" -dependencies = [ - "ryu-js", - "serde", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_transforms_base", - "swc_ecma_transforms_react", - "swc_ecma_utils", - "swc_ecma_visit", -] - -[[package]] -name = "swc_ecma_utils" -version = "0.134.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029eec7dd485923a75b5a45befd04510288870250270292fc2c1b3a9e7547408" -dependencies = [ - "indexmap 2.7.0", - "num_cpus", - "once_cell", - "rustc-hash 1.1.0", - "ryu-js", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_ecma_visit", - "tracing", - "unicode-id", -] - -[[package]] -name = "swc_ecma_visit" -version = "0.104.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1c6802e68e51f336e8bc9644e9ff9da75d7da9c1a6247d532f2e908aa33e81" -dependencies = [ - "new_debug_unreachable", - "num-bigint", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "swc_visit", - "tracing", -] - -[[package]] -name = "swc_eq_ignore_macros" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "swc_macros_common" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - -[[package]] -name = "swc_visit" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceb044142ba2719ef9eb3b6b454fce61ab849eb696c34d190f04651955c613d" -dependencies = [ - "either", - "new_debug_unreachable", -] - -[[package]] -name = "swc_visit_macros" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92807d840959f39c60ce8a774a3f83e8193c658068e6d270dbe0a05e40e90b41" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "swc_macros_common", - "syn 2.0.101", -] - [[package]] name = "syn" version = "1.0.109" @@ -10515,6 +10319,17 @@ dependencies = [ "libc", ] +[[package]] +name = "sys_traits" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3374191d43a934854e99a46cd47f8124369e690353e0f8db42769218d083690" +dependencies = [ + "getrandom 0.2.15", + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -10528,6 +10343,12 @@ dependencies = [ "version-compare", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tantivy" version = "0.22.0" @@ -10681,6 +10502,12 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +[[package]] +name = "target-lexicon" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" + [[package]] name = "tempfile" version = "3.14.0" @@ -10703,15 +10530,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "text_lines" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd5828de7deaa782e1dd713006ae96b3bee32d3279b79eb67ecf8072c059bcf" -dependencies = [ - "serde", -] - [[package]] name = "thiserror" version = "1.0.69" @@ -10723,11 +10541,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.8" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.8", + "thiserror-impl 2.0.12", ] [[package]] @@ -10743,9 +10561,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.8" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -10980,6 +10798,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-vsock" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1824fc0300433f400df6b6264a9ab00ba93f39d38c3157fb5f05183476c4af10" +dependencies = [ + "bytes", + "futures", + "libc", + "tokio", + "vsock", +] + [[package]] name = "toml" version = "0.8.19" @@ -11007,7 +10838,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.7.0", + "indexmap 2.9.0", "toml_datetime", "winnow 0.5.40", ] @@ -11018,7 +10849,7 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.7.0", + "indexmap 2.9.0", "toml_datetime", "winnow 0.5.40", ] @@ -11029,7 +10860,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.7.0", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -11051,7 +10882,7 @@ dependencies = [ "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.2", + "hyper 1.6.0", "hyper-timeout", "hyper-util", "percent-encoding", @@ -11091,7 +10922,7 @@ dependencies = [ "indexmap 1.9.3", "pin-project", "pin-project-lite", - "rand", + "rand 0.8.5", "slab", "tokio", "tokio-util", @@ -11110,6 +10941,7 @@ dependencies = [ "futures-util", "pin-project-lite", "sync_wrapper", + "tokio", "tower-layer", "tower-service", ] @@ -11121,7 +10953,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "async-compression", - "bitflags 2.6.0", + "bitflags 2.9.1", "bytes", "futures-core", "http 1.2.0", @@ -11241,16 +11073,6 @@ dependencies = [ "petgraph", ] -[[package]] -name = "triomphe" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" -dependencies = [ - "serde", - "stable_deref_trait", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -11282,16 +11104,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "rand", + "rand 0.8.5", "static_assertions", ] -[[package]] -name = "typed-arena" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" - [[package]] name = "typed-path" version = "0.10.0" @@ -11386,12 +11202,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" -[[package]] -name = "unicode-id" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" - [[package]] name = "unicode-id-start" version = "1.3.1" @@ -11410,15 +11220,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-properties" version = "0.1.3" @@ -11455,12 +11256,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - [[package]] name = "universal-hash" version = "0.5.1" @@ -11602,23 +11397,22 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ - "getrandom", + "getrandom 0.2.15", "serde", ] [[package]] name = "v8" -version = "130.0.2" +version = "137.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee0be58935708fa4d7efb970c6cf9f2d9511d24ee24246481a65b6ee167348d" +checksum = "be127b878f582d3b7602bd2e26a39f90a95b388deb940da7f6554617aedcdf40" dependencies = [ "bindgen", - "bitflags 2.6.0", + "bitflags 2.9.1", "fslock", "gzip-header", "home", - "miniz_oxide 0.7.4", - "once_cell", + "miniz_oxide 0.8.8", "paste", "which 6.0.3", ] @@ -11629,9 +11423,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97599c400fc79925922b58303e98fcb8fa88f573379a08ddb652e72cbd2e70f6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "encoding_rs", - "indexmap 2.7.0", + "indexmap 2.9.0", "num-bigint", "serde", "thiserror 1.0.69", @@ -11749,6 +11543,16 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" +[[package]] +name = "vsock" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8b4d00e672f147fc86a09738fadb1445bd1c0a40542378dfb82909deeee688" +dependencies = [ + "libc", + "nix 0.29.0", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -11774,6 +11578,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasite" version = "0.1.0" @@ -11782,20 +11595,21 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", @@ -11807,9 +11621,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -11820,9 +11634,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -11830,9 +11644,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -11843,9 +11657,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-timer" @@ -11864,11 +11681,12 @@ dependencies = [ [[package]] name = "wasm_dep_analyzer" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f270206a91783fd90625c8bb0d8fbd459d0b1d1bf209b656f713f01ae7c04b8" +checksum = "2eeee3bdea6257cc36d756fa745a70f9d393571e47d69e0ed97581676a5369ca" dependencies = [ - "thiserror 1.0.69", + "deno_error", + "thiserror 2.0.12", ] [[package]] @@ -11876,7 +11694,7 @@ name = "waycrate_xkbkeycode" version = "0.13.99" source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13#de88e8d3be5e7ebef6d8456ea62eed26e503bf92" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "calloop 0.14.2", "memmap2 0.9.5", "smol_str", @@ -11906,7 +11724,7 @@ version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "rustix", "wayland-backend", "wayland-scanner", @@ -11918,7 +11736,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "cursor-icon", "wayland-backend", ] @@ -11940,7 +11758,7 @@ version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -11952,7 +11770,7 @@ version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -11965,7 +11783,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2e42969764e469a115d4bb1c16e9588ef8b75b127ba7a2c9ddf1e140b25ca7" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols 0.32.5", @@ -11978,7 +11796,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols 0.32.5", @@ -11991,7 +11809,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols 0.31.2", @@ -12004,7 +11822,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols 0.32.5", @@ -12029,7 +11847,7 @@ version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c89532cc712a2adb119eb4d09694b402576052254d0bb284f82ac1c47fb786ad" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "downcast-rs", "io-lifetimes", "rustix", @@ -12051,9 +11869,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -12069,6 +11887,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-transport-proto" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1814af4572856a29a2d29a56520e86fda994423043b70139ce98e5a32e0d91be" +dependencies = [ + "bytes", + "http 1.2.0", + "thiserror 2.0.12", + "url", +] + [[package]] name = "webpki-root-certs" version = "0.26.7" @@ -12118,35 +11948,6 @@ dependencies = [ "wgpu-types 23.0.0", ] -[[package]] -name = "wgpu-core" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39" -dependencies = [ - "arrayvec", - "bit-vec 0.6.3", - "bitflags 2.6.0", - "cfg_aliases 0.1.1", - "codespan-reporting", - "document-features", - "indexmap 2.7.0", - "log", - "naga 0.20.0", - "once_cell", - "parking_lot 0.12.3", - "profiling", - "raw-window-handle", - "ron", - "rustc-hash 1.1.0", - "serde", - "smallvec", - "thiserror 1.0.69", - "web-sys", - "wgpu-hal 0.21.1", - "wgpu-types 0.20.0", -] - [[package]] name = "wgpu-core" version = "23.0.1" @@ -12154,11 +11955,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a" dependencies = [ "arrayvec", - "bit-vec 0.8.0", - "bitflags 2.6.0", + "bit-vec", + "bitflags 2.9.1", "cfg_aliases 0.1.1", "document-features", - "indexmap 2.7.0", + "indexmap 2.9.0", "log", "naga 23.1.0", "once_cell", @@ -12173,45 +11974,30 @@ dependencies = [ ] [[package]] -name = "wgpu-hal" -version = "0.21.1" +name = "wgpu-core" +version = "24.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222" +checksum = "7f0aa306497a238d169b9dc70659105b4a096859a34894544ca81719242e1499" dependencies = [ - "android_system_properties", "arrayvec", - "ash 0.37.3+1.3.251", - "bit-set 0.5.3", - "bitflags 2.6.0", - "block", - "cfg_aliases 0.1.1", - "core-graphics-types 0.1.3", - "d3d12", - "glow 0.13.1", - "glutin_wgl_sys 0.5.0", - "gpu-alloc", - "gpu-descriptor", - "js-sys", - "khronos-egl", - "libc", - "libloading 0.8.6", + "bit-vec", + "bitflags 2.9.1", + "cfg_aliases 0.2.1", + "document-features", + "indexmap 2.9.0", "log", - "metal 0.28.0", - "naga 0.20.0", - "ndk-sys 0.5.0+25.2.9519653", - "objc", + "naga 24.0.0", "once_cell", "parking_lot 0.12.3", "profiling", - "range-alloc", "raw-window-handle", + "ron", "rustc-hash 1.1.0", + "serde", "smallvec", - "thiserror 1.0.69", - "wasm-bindgen", - "web-sys", - "wgpu-types 0.20.0", - "winapi", + "thiserror 2.0.12", + "wgpu-hal 24.0.4", + "wgpu-types 24.0.0", ] [[package]] @@ -12222,15 +12008,15 @@ checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821" dependencies = [ "android_system_properties", "arrayvec", - "ash 0.38.0+1.3.281", - "bit-set 0.8.0", - "bitflags 2.6.0", + "ash", + "bit-set", + "bitflags 2.9.1", "block", "bytemuck", "cfg_aliases 0.1.1", "core-graphics-types 0.1.3", "glow 0.14.2", - "glutin_wgl_sys 0.6.0", + "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", "gpu-descriptor", @@ -12255,20 +12041,53 @@ dependencies = [ "wasm-bindgen", "web-sys", "wgpu-types 23.0.0", - "windows", + "windows 0.58.0", "windows-core 0.58.0", ] [[package]] -name = "wgpu-types" -version = "0.20.0" +name = "wgpu-hal" +version = "24.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" +checksum = "f112f464674ca69f3533248508ee30cb84c67cf06c25ff6800685f5e0294e259" dependencies = [ - "bitflags 2.6.0", + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.9.1", + "block", + "bytemuck", + "cfg_aliases 0.2.1", + "core-graphics-types 0.1.3", + "glow 0.16.0", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", "js-sys", - "serde", + "khronos-egl", + "libc", + "libloading 0.8.6", + "log", + "metal 0.31.0", + "naga 24.0.0", + "ndk-sys 0.5.0+25.2.9519653", + "objc", + "once_cell", + "ordered-float 4.6.0", + "parking_lot 0.12.3", + "profiling", + "range-alloc", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.12", + "wasm-bindgen", "web-sys", + "wgpu-types 24.0.0", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] @@ -12277,21 +12096,22 @@ version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "js-sys", "web-sys", ] [[package]] -name = "which" -version = "4.4.2" +name = "wgpu-types" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" dependencies = [ - "either", - "home", - "once_cell", - "rustix", + "bitflags 2.9.1", + "js-sys", + "log", + "serde", + "web-sys", ] [[package]] @@ -12390,6 +12210,28 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -12405,13 +12247,37 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -12423,6 +12289,17 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -12434,6 +12311,33 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link", +] + [[package]] name = "windows-result" version = "0.2.0" @@ -12443,16 +12347,34 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -12535,6 +12457,15 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -12675,7 +12606,7 @@ dependencies = [ "ahash 0.8.11", "android-activity", "atomic-waker", - "bitflags 2.6.0", + "bitflags 2.9.1", "block2", "bytemuck", "calloop 0.13.0", @@ -12761,6 +12692,15 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + [[package]] name = "wl-clipboard-rs" version = "0.8.1" @@ -12847,7 +12787,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", - "rand_core", + "rand_core 0.6.4", "serde", "zeroize", ] @@ -12908,7 +12848,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.1", "dlib", "log", "once_cell", @@ -12990,7 +12930,7 @@ dependencies = [ "hex", "nix 0.29.0", "ordered-stream", - "rand", + "rand 0.8.5", "serde", "serde_repr", "sha1", diff --git a/Cargo.toml b/Cargo.toml index ca03a94..2611c38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,3 @@ inherits = "release" opt-level = "s" lto = "thin" strip = true - -[patch.crates-io] -# NOTE https://github.com/ipetkov/crane/issues/336 -libffi-sys = { git = "https://github.com/tov/libffi-rs", rev = "d0704d634b6f3ffef5b6fc7e07fe965a1cff5c7b" } diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml index d626344..d17d09f 100644 --- a/rust/common/Cargo.toml +++ b/rust/common/Cargo.toml @@ -12,7 +12,6 @@ anyhow.workspace = true tokio.workspace = true serde.workspace = true serde_json.workspace = true -thiserror.workspace = true bincode.workspace = true tonic.workspace = true prost.workspace = true diff --git a/rust/plugin_runtime/Cargo.toml b/rust/plugin_runtime/Cargo.toml index 6f60c7c..21772dd 100644 --- a/rust/plugin_runtime/Cargo.toml +++ b/rust/plugin_runtime/Cargo.toml @@ -12,6 +12,7 @@ gauntlet-common-plugin-runtime = { path = "../common_plugin_runtime" } # shared anyhow.workspace = true +thiserror.workspace = true tracing.workspace = true tokio.workspace = true tokio-util.workspace = true @@ -27,9 +28,14 @@ walkdir.workspace = true typed-path.workspace = true interprocess.workspace = true +# deno crates +deno_core = { version = "0.347.0" } # https://github.com/denoland/deno/blob/v2.3.3 +deno_runtime = { version = "0.213.0", features = ["transpile"] } +deno_error = { version = "*" } +deno_resolver = "*" +sys_traits = "*" + # other -deno_core = { version = "0.321.0" } # deno 2.1.1 -deno_runtime = { version = "0.188.0" } resvg = { version = "0.44.0", default-features = false} numbat = "1.14.0" which = "7.0.1" diff --git a/rust/plugin_runtime/src/assets.rs b/rust/plugin_runtime/src/assets.rs index f5f9f74..286802b 100644 --- a/rust/plugin_runtime/src/assets.rs +++ b/rust/plugin_runtime/src/assets.rs @@ -7,9 +7,11 @@ use deno_core::op2; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; +use crate::deno::GauntletJsError; + #[op2(async)] #[buffer] -pub async fn asset_data(state: Rc>, #[string] path: String) -> anyhow::Result> { +pub async fn asset_data(state: Rc>, #[string] path: String) -> Result, GauntletJsError> { let api = { let state = state.borrow(); @@ -25,7 +27,7 @@ pub async fn asset_data(state: Rc>, #[string] path: String) -> #[op2] #[buffer] -pub fn asset_data_blocking(state: Rc>, #[string] path: String) -> anyhow::Result> { +pub fn asset_data_blocking(state: Rc>, #[string] path: String) -> Result, GauntletJsError> { let api = { let state = state.borrow(); diff --git a/rust/plugin_runtime/src/clipboard.rs b/rust/plugin_runtime/src/clipboard.rs index c11bb30..a876f9e 100644 --- a/rust/plugin_runtime/src/clipboard.rs +++ b/rust/plugin_runtime/src/clipboard.rs @@ -7,12 +7,13 @@ use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use gauntlet_common_plugin_runtime::model::JsClipboardData; +use crate::deno::GauntletJsError; use crate::model::DenoInClipboardData; use crate::model::DenoOutClipboardData; #[op2(async)] #[serde] -pub async fn clipboard_read(state: Rc>) -> anyhow::Result { +pub async fn clipboard_read(state: Rc>) -> Result { let api = { let state = state.borrow(); @@ -31,7 +32,7 @@ pub async fn clipboard_read(state: Rc>) -> anyhow::Result>) -> anyhow::Result> { +pub async fn clipboard_read_text(state: Rc>) -> Result, GauntletJsError> { let api = { let state = state.borrow(); @@ -44,7 +45,10 @@ pub async fn clipboard_read_text(state: Rc>) -> anyhow::Result< } #[op2(async)] -pub async fn clipboard_write(state: Rc>, #[serde] data: DenoInClipboardData) -> anyhow::Result<()> { +pub async fn clipboard_write( + state: Rc>, + #[serde] data: DenoInClipboardData, +) -> Result<(), GauntletJsError> { let api = { let state = state.borrow(); @@ -62,7 +66,7 @@ pub async fn clipboard_write(state: Rc>, #[serde] data: DenoInC } #[op2(async)] -pub async fn clipboard_write_text(state: Rc>, #[string] data: String) -> anyhow::Result<()> { +pub async fn clipboard_write_text(state: Rc>, #[string] data: String) -> Result<(), GauntletJsError> { let api = { let state = state.borrow(); @@ -75,7 +79,7 @@ pub async fn clipboard_write_text(state: Rc>, #[string] data: S } #[op2(async)] -pub async fn clipboard_clear(state: Rc>) -> anyhow::Result<()> { +pub async fn clipboard_clear(state: Rc>) -> Result<(), GauntletJsError> { let api = { let state = state.borrow(); diff --git a/rust/plugin_runtime/src/deno.rs b/rust/plugin_runtime/src/deno.rs index d1bf833..aa8d9cd 100644 --- a/rust/plugin_runtime/src/deno.rs +++ b/rust/plugin_runtime/src/deno.rs @@ -6,7 +6,6 @@ use std::rc::Rc; use std::sync::Arc; use anyhow::Context; -use anyhow::anyhow; use deno_core::FastString; use deno_core::ModuleLoadResponse; use deno_core::ModuleLoader; @@ -17,12 +16,17 @@ use deno_core::ModuleType; use deno_core::RequestedModuleType; use deno_core::ResolutionKind; use deno_core::StaticModuleLoader; +use deno_core::error::ModuleLoaderError; +use deno_core::thiserror; +use deno_core::url::ParseError; use deno_core::url::Url; -use deno_runtime::BootstrapOptions; -use deno_runtime::deno_fs::FileSystem; +use deno_error::JsErrorBox; +use deno_resolver::npm::ByonmInNpmPackageChecker; +use deno_resolver::npm::ManagedNpmResolver; use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_io::Stdio; use deno_runtime::deno_io::StdioPipe; +use deno_runtime::deno_node::NodeExtInitServices; use deno_runtime::worker::MainWorker; use deno_runtime::worker::WorkerOptions; use deno_runtime::worker::WorkerServiceOptions; @@ -30,8 +34,10 @@ use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use gauntlet_common_plugin_runtime::model::JsEvent; use gauntlet_common_plugin_runtime::model::JsInit; use gauntlet_common_plugin_runtime::model::JsPluginCode; +use gauntlet_utils::channel::RequestError; use once_cell::sync::Lazy; use regex::Regex; +use sys_traits::impls::RealSys; use tokio::runtime::Handle; use tokio::sync::mpsc::Receiver; @@ -106,6 +112,16 @@ impl CustomModuleLoader { } } +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum GauntletJsError { + #[class(generic)] + #[error(transparent)] + Anyhow(#[from] anyhow::Error), + #[class(generic)] + #[error(transparent)] + Request(#[from] RequestError), +} + const MODULES: [(&str, &str); 11] = [ ( "gauntlet:init", @@ -189,7 +205,7 @@ impl ModuleLoader for CustomModuleLoader { specifier: &str, referrer: &str, _kind: ResolutionKind, - ) -> Result { + ) -> Result { static PLUGIN_ENTRYPOINT_PATTERN: Lazy = Lazy::new(|| { Regex::new(r"^gauntlet:entrypoint\?(?[a-zA-Z0-9_-]+)$").expect("invalid regex") }); @@ -199,12 +215,18 @@ impl ModuleLoader for CustomModuleLoader { Lazy::new(|| Regex::new(r"^\./(?[a-zA-Z0-9_-]+)\.js$").expect("invalid regex")); if PLUGIN_ENTRYPOINT_PATTERN.is_match(specifier) { - return Ok(specifier.parse()?); + return Ok(specifier + .parse() + .map_err(|err: ParseError| ModuleLoaderError::Core(err.into()))?); } if PLUGIN_ENTRYPOINT_PATTERN.is_match(referrer) || PLUGIN_MODULE_PATTERN.is_match(referrer) { if let Some(captures) = PATH_PATTERN.captures(specifier) { - return Ok(format!("gauntlet:module?{}", &captures["js_module"]).parse()?); + let result = format!("gauntlet:module?{}", &captures["js_module"]) + .parse() + .map_err(|err: ParseError| ModuleLoaderError::Core(err.into()))?; + + return Ok(result); } } @@ -221,15 +243,17 @@ impl ModuleLoader for CustomModuleLoader { ("@project-gauntlet/api/hooks", _) => "gauntlet:bridge/hooks", ("@project-gauntlet/api/helpers", _) => "gauntlet:bridge/helpers", _ => { - return Err(anyhow!( + let error = JsErrorBox::generic(format!( "Illegal import with specifier '{}' and referrer '{}'", - specifier, - referrer + specifier, referrer )); + return Err(error.into()); } }; - Ok(Url::parse(specifier)?) + let url = Url::parse(specifier).map_err(|err: ParseError| ModuleLoaderError::Core(err.into()))?; + + Ok(url) } fn load( @@ -249,13 +273,19 @@ impl ModuleLoader for CustomModuleLoader { } "gauntlet:entrypoint" | "gauntlet:module" => { match module_specifier.query() { - None => ModuleLoadResponse::Sync(Err(anyhow!("Module specifier doesn't have query part"))), + None => { + let error = JsErrorBox::generic("Module specifier doesn't have query part"); + + ModuleLoadResponse::Sync(Err(error.into())) + } Some(entrypoint_id) => { let result = self .code .js .get(entrypoint_id) - .ok_or(anyhow!("Cannot find JS code path: {:?}", entrypoint_id)) + .ok_or_else(|| { + JsErrorBox::generic(format!("Cannot find JS code path: {:?}", entrypoint_id)).into() + }) .map(|js| ModuleSourceCode::String(js.clone().into())) .map(|js| ModuleSource::new(ModuleType::JavaScript, js, module_specifier, None)); @@ -268,11 +298,13 @@ impl ModuleLoader for CustomModuleLoader { self.static_loader .load(module_specifier, maybe_referrer, is_dyn_import, requested_module_type) } else { - ModuleLoadResponse::Sync(Err(anyhow!( + let error = JsErrorBox::generic(format!( "Module not found: specifier '{}' and referrer '{:?}'", specifier, maybe_referrer.map(|url| url.as_str()) - ))) + )); + + ModuleLoadResponse::Sync(Err(error.into())) } } } @@ -493,12 +525,9 @@ pub async fn start_js_runtime( let init_url: ModuleSpecifier = "gauntlet:init".parse().expect("should be valid"); - let fs: Arc = Arc::new(RealFs); - let home_dir = PathBuf::from(init.home_dir); let permissions_container = permissions_to_deno( - fs.clone(), &init.permissions, &home_dir, Path::new(&init.plugin_data_dir), @@ -506,13 +535,13 @@ pub async fn start_js_runtime( )?; let gauntlet_esm = if cfg!(feature = "release") && !init.dev_plugin { - prod::gauntlet_esm::init_ops_and_esm() + prod::gauntlet_esm::init() } else { - dev::gauntlet_esm::init_ops_and_esm() + dev::gauntlet_esm::init() }; let mut extensions = vec![ - gauntlet::init_ops( + gauntlet::init( EventReceiver::new(event_stream), PluginData::new( init.plugin_id.clone(), @@ -531,7 +560,7 @@ pub async fn start_js_runtime( ]; if init.plugin_id.to_string() == "bundled://gauntlet" { - extensions.push(gauntlet_internal_all::init_ops_and_esm( + extensions.push(gauntlet_internal_all::init( NumbatContext::new(), ApplicationContext::new()?, )); @@ -540,19 +569,20 @@ pub async fn start_js_runtime( extensions.push(gauntlet_internal_macos::init_ops_and_esm()); #[cfg(target_os = "linux")] - extensions.push(crate::plugins::applications::gauntlet_internal_linux::init_ops_and_esm()); + extensions.push(crate::plugins::applications::gauntlet_internal_linux::init()); #[cfg(target_os = "windows")] extensions.push(crate::plugins::applications::gauntlet_internal_windows::init_ops_and_esm()); } let mut worker = MainWorker::bootstrap_from_options( - init_url.clone(), - WorkerServiceOptions { + &init_url, + WorkerServiceOptions::, RealSys> { blob_store: Arc::new(Default::default()), broadcast_channel: Default::default(), + deno_rt_native_addon_loader: None, feature_checker: Arc::new(Default::default()), - fs, + fs: Arc::new(RealFs), module_loader: Rc::new(CustomModuleLoader::new(init.code, init.dev_plugin)), node_services: None, npm_process_state_provider: None, @@ -564,11 +594,6 @@ pub async fn start_js_runtime( v8_code_cache: None, }, WorkerOptions { - bootstrap: BootstrapOptions { - is_stderr_tty: false, - is_stdout_tty: false, - ..Default::default() - }, extensions, maybe_inspector_server: None, should_wait_for_inspector_session: false, diff --git a/rust/plugin_runtime/src/entrypoint_generators.rs b/rust/plugin_runtime/src/entrypoint_generators.rs index ae10b0c..e1f6dbf 100644 --- a/rust/plugin_runtime/src/entrypoint_generators.rs +++ b/rust/plugin_runtime/src/entrypoint_generators.rs @@ -6,9 +6,13 @@ use deno_core::op2; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; +use crate::deno::GauntletJsError; + #[op2(async)] #[serde] -pub async fn get_entrypoint_generator_entrypoint_ids(state: Rc>) -> anyhow::Result> { +pub async fn get_entrypoint_generator_entrypoint_ids( + state: Rc>, +) -> Result, GauntletJsError> { let api = { let state = state.borrow(); diff --git a/rust/plugin_runtime/src/events.rs b/rust/plugin_runtime/src/events.rs index d007385..9020e87 100644 --- a/rust/plugin_runtime/src/events.rs +++ b/rust/plugin_runtime/src/events.rs @@ -7,6 +7,8 @@ use deno_core::op2; use gauntlet_common_plugin_runtime::model::JsEvent; use tokio::sync::mpsc::Receiver; +use crate::deno::GauntletJsError; + pub struct EventReceiver { event_stream: Rc>>, } @@ -21,7 +23,7 @@ impl EventReceiver { #[op2(async)] #[serde] -pub async fn op_plugin_get_pending_event(state: Rc>) -> anyhow::Result { +pub async fn op_plugin_get_pending_event(state: Rc>) -> Result { let event_stream = { state.borrow().borrow::().event_stream.clone() }; let mut event_stream = event_stream.borrow_mut(); diff --git a/rust/plugin_runtime/src/lib.rs b/rust/plugin_runtime/src/lib.rs index 224b8cd..7d44b5c 100644 --- a/rust/plugin_runtime/src/lib.rs +++ b/rust/plugin_runtime/src/lib.rs @@ -115,7 +115,7 @@ async fn run_outer(socket_name: String) -> anyhow::Result<()> { tracing::error!("Request loop has unexpectedly stopped {:?}", plugin_id) } _ = { - run_new_tokio(handle, stop_token.clone(), init, event_receiver, api) + run_new_tokio(handle, stop_token.clone(), init, event_receiver, api.clone()) } => { tracing::error!("Request loop has unexpectedly stopped {:?}", plugin_id) } @@ -130,7 +130,7 @@ async fn run_outer(socket_name: String) -> anyhow::Result<()> { tracing::debug!("Plugin runtime outer loop has been stopped {:?}", plugin_id); - drop((recver, sender)); + drop((recver, sender, api)); Ok(()) } diff --git a/rust/plugin_runtime/src/logs.rs b/rust/plugin_runtime/src/logs.rs index 3dd34c3..d9a476c 100644 --- a/rust/plugin_runtime/src/logs.rs +++ b/rust/plugin_runtime/src/logs.rs @@ -4,6 +4,7 @@ use std::rc::Rc; use deno_core::OpState; use deno_core::op2; +use crate::deno::GauntletJsError; use crate::plugin_data::PluginData; #[op2(fast)] @@ -11,7 +12,7 @@ pub fn op_log_trace( state: Rc>, #[string] target: String, #[string] message: String, -) -> anyhow::Result<()> { +) -> Result<(), GauntletJsError> { let plugin_id = state.borrow().borrow::().plugin_id().to_string(); tracing::trace!(target = target, plugin_id = plugin_id, message); @@ -24,7 +25,7 @@ pub fn op_log_debug( state: Rc>, #[string] target: String, #[string] message: String, -) -> anyhow::Result<()> { +) -> Result<(), GauntletJsError> { let plugin_id = state.borrow().borrow::().plugin_id().to_string(); tracing::debug!(target = target, plugin_id = plugin_id, message); @@ -37,7 +38,7 @@ pub fn op_log_info( state: Rc>, #[string] target: String, #[string] message: String, -) -> anyhow::Result<()> { +) -> Result<(), GauntletJsError> { let plugin_id = state.borrow().borrow::().plugin_id().to_string(); tracing::info!(target = target, plugin_id = plugin_id, message); @@ -50,7 +51,7 @@ pub fn op_log_warn( state: Rc>, #[string] target: String, #[string] message: String, -) -> anyhow::Result<()> { +) -> Result<(), GauntletJsError> { let plugin_id = state.borrow().borrow::().plugin_id().to_string(); tracing::warn!(target = target, plugin_id = plugin_id, message); @@ -63,7 +64,7 @@ pub fn op_log_error( state: Rc>, #[string] target: String, #[string] message: String, -) -> anyhow::Result<()> { +) -> Result<(), GauntletJsError> { let plugin_id = state.borrow().borrow::().plugin_id().to_string(); tracing::error!(target = target, plugin_id = plugin_id, message); diff --git a/rust/plugin_runtime/src/permissions.rs b/rust/plugin_runtime/src/permissions.rs index 27fd52e..417a7d2 100644 --- a/rust/plugin_runtime/src/permissions.rs +++ b/rust/plugin_runtime/src/permissions.rs @@ -5,7 +5,6 @@ use std::path::PathBuf; use std::sync::Arc; use anyhow::anyhow; -use deno_runtime::deno_fs::FileSystemRc; use deno_runtime::deno_permissions::AllowRunDescriptor; use deno_runtime::deno_permissions::EnvDescriptor; use deno_runtime::deno_permissions::EnvQueryDescriptor; @@ -25,14 +24,13 @@ use gauntlet_common_plugin_runtime::model::JsPluginPermissionsExec; use typed_path::Utf8TypedPath; pub fn permissions_to_deno( - fs: FileSystemRc, permissions: &JsPluginPermissions, home_dir: &Path, plugin_data_dir: &Path, plugin_cache_dir: &Path, ) -> anyhow::Result { Ok(PermissionsContainer::new( - Arc::new(RuntimePermissionDescriptorParser::new(fs)), + Arc::new(RuntimePermissionDescriptorParser::new(sys_traits::impls::RealSys)), Permissions { read: path_permission( &permissions.filesystem.read, diff --git a/rust/plugin_runtime/src/plugins/applications/linux/mod.rs b/rust/plugin_runtime/src/plugins/applications/linux/mod.rs index 236cd38..ab5429a 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/mod.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/mod.rs @@ -3,6 +3,7 @@ use std::path::Path; use std::path::PathBuf; use std::rc::Rc; +use anyhow::anyhow; use deno_core::OpState; use deno_core::op2; use freedesktop_entry_parser::parse_entry; @@ -10,6 +11,7 @@ use freedesktop_icons::lookup; use gauntlet_common::detached_process::CommandExt; use tokio::task::spawn_blocking; +use crate::deno::GauntletJsError; use crate::plugin_data::PluginData; use crate::plugins::applications::DesktopApplication; use crate::plugins::applications::DesktopPathAction; @@ -67,7 +69,7 @@ impl LinuxDesktopEnvironment { async fn linux_app_from_path( state: Rc>, #[string] path: String, -) -> anyhow::Result> { +) -> Result, GauntletJsError> { let home_dir = { let state = state.borrow(); @@ -76,7 +78,11 @@ async fn linux_app_from_path( home_dir }; - Ok(spawn_blocking(|| linux_app_from_path_async(home_dir, PathBuf::from(path))).await?) + let result = spawn_blocking(|| linux_app_from_path_async(home_dir, PathBuf::from(path))) + .await + .map_err(|err| anyhow!(err))?; + + Ok(result) } #[op2] @@ -97,10 +103,11 @@ fn linux_application_dirs(state: Rc>) -> Vec { } #[op2(fast)] -fn linux_open_application(#[string] desktop_file_id: String) -> anyhow::Result<()> { +fn linux_open_application(#[string] desktop_file_id: String) -> Result<(), GauntletJsError> { std::process::Command::new("gtk-launch") .args([desktop_file_id]) - .spawn_detached()?; + .spawn_detached() + .map_err(|err| anyhow!(err))?; Ok(()) } diff --git a/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs b/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs index ec830f0..587ae35 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/wayland/mod.rs @@ -27,6 +27,7 @@ use wayland_client::globals::registry_queue_init; use wayland_client::protocol::wl_registry; use wayland_client::protocol::wl_seat::WlSeat; +use crate::deno::GauntletJsError; use crate::plugins::applications::ApplicationContext; use crate::plugins::applications::DesktopEnvironment; use crate::plugins::applications::linux; @@ -164,7 +165,10 @@ fn run_wayland_client( } #[op2(fast)] -pub fn linux_wayland_focus_window(state: Rc>, #[string] window_uuid: String) -> anyhow::Result<()> { +pub fn linux_wayland_focus_window( + state: Rc>, + #[string] window_uuid: String, +) -> Result<(), GauntletJsError> { { let state = state.borrow(); @@ -208,7 +212,7 @@ fn activation_handler(event: Event, _metadata: &mut (), state: &mut Wayl #[serde] pub async fn application_wayland_pending_event( state: Rc>, -) -> anyhow::Result { +) -> Result { let receiver = { let state = state.borrow(); diff --git a/rust/plugin_runtime/src/plugins/applications/linux/x11.rs b/rust/plugin_runtime/src/plugins/applications/linux/x11.rs index 589b5c1..a7363e3 100644 --- a/rust/plugin_runtime/src/plugins/applications/linux/x11.rs +++ b/rust/plugin_runtime/src/plugins/applications/linux/x11.rs @@ -28,6 +28,7 @@ use x11rb::protocol::xproto::MapState; use x11rb::protocol::xproto::Window; use x11rb::rust_connection::RustConnection; +use crate::deno::GauntletJsError; use crate::plugins::applications::ApplicationContext; use crate::plugins::applications::DesktopEnvironment; use crate::plugins::applications::linux; @@ -136,7 +137,9 @@ pub enum JSX11WindowType { #[op2(async)] #[serde] -pub async fn application_x11_pending_event(state: Rc>) -> anyhow::Result { +pub async fn application_x11_pending_event( + state: Rc>, +) -> Result { let receiver = { let state = state.borrow(); @@ -162,7 +165,7 @@ pub async fn application_x11_pending_event(state: Rc>) -> anyho } #[op2(fast)] -pub fn linux_x11_focus_window(#[string] x11_window_id: String) -> anyhow::Result<()> { +pub fn linux_x11_focus_window(#[string] x11_window_id: String) -> Result<(), GauntletJsError> { focus_window(x11_window_id)?; Ok(()) diff --git a/rust/plugin_runtime/src/plugins/numbat.rs b/rust/plugin_runtime/src/plugins/numbat.rs index e28b22a..cef1e41 100644 --- a/rust/plugin_runtime/src/plugins/numbat.rs +++ b/rust/plugin_runtime/src/plugins/numbat.rs @@ -13,6 +13,8 @@ use numbat::pretty_print::PrettyPrint; use numbat::resolver::CodeSource; use serde::Serialize; +use crate::deno::GauntletJsError; + #[derive(Clone)] pub struct NumbatContext(Rc>); @@ -40,7 +42,7 @@ struct NumbatResult { #[op2] #[serde] -pub fn run_numbat(state: Rc>, #[string] input: String) -> anyhow::Result { +pub fn run_numbat(state: Rc>, #[string] input: String) -> Result { let context = { let state = state.borrow(); @@ -51,7 +53,9 @@ pub fn run_numbat(state: Rc>, #[string] input: String) -> anyho let mut context = context.0.borrow_mut(); - let (statements, result) = context.interpret(&input, CodeSource::Text)?; + let (statements, result) = context + .interpret(&input, CodeSource::Text) + .map_err(|err| anyhow!(err))?; let formatter = PlainTextFormatter; diff --git a/rust/plugin_runtime/src/plugins/settings.rs b/rust/plugin_runtime/src/plugins/settings.rs index 7a2a1d8..1a89536 100644 --- a/rust/plugin_runtime/src/plugins/settings.rs +++ b/rust/plugin_runtime/src/plugins/settings.rs @@ -1,11 +1,17 @@ +use anyhow::anyhow; use deno_core::op2; use gauntlet_common::detached_process::CommandExt; +use crate::deno::GauntletJsError; + #[op2(fast)] -pub fn open_settings() -> anyhow::Result<()> { - std::process::Command::new(std::env::current_exe()?) +pub fn open_settings() -> Result<(), GauntletJsError> { + let current_exe = std::env::current_exe().map_err(|err| anyhow!(err))?; + + std::process::Command::new(current_exe) .args(["settings"]) - .spawn_detached()?; + .spawn_detached() + .map_err(|err| anyhow!(err))?; Ok(()) } diff --git a/rust/plugin_runtime/src/preferences.rs b/rust/plugin_runtime/src/preferences.rs index 294a512..72376c2 100644 --- a/rust/plugin_runtime/src/preferences.rs +++ b/rust/plugin_runtime/src/preferences.rs @@ -10,9 +10,13 @@ use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use gauntlet_common_plugin_runtime::model::JsPreferenceUserData; +use crate::deno::GauntletJsError; + #[op2] #[serde] -pub fn get_plugin_preferences(state: Rc>) -> anyhow::Result> { +pub fn get_plugin_preferences( + state: Rc>, +) -> Result, GauntletJsError> { let api = { let state = state.borrow(); @@ -29,7 +33,7 @@ pub fn get_plugin_preferences(state: Rc>) -> anyhow::Result>, #[string] entrypoint_id: &str, -) -> anyhow::Result> { +) -> Result, GauntletJsError> { let api = { let state = state.borrow(); @@ -46,7 +50,7 @@ pub fn get_entrypoint_preferences( } #[op2(async)] -pub async fn plugin_preferences_required(state: Rc>) -> anyhow::Result { +pub async fn plugin_preferences_required(state: Rc>) -> Result { let api = { let state = state.borrow(); @@ -62,7 +66,7 @@ pub async fn plugin_preferences_required(state: Rc>) -> anyhow: pub async fn entrypoint_preferences_required( state: Rc>, #[string] entrypoint_id: String, -) -> anyhow::Result { +) -> Result { let api = { let state = state.borrow(); diff --git a/rust/plugin_runtime/src/search.rs b/rust/plugin_runtime/src/search.rs index 26c1e0c..32a6719 100644 --- a/rust/plugin_runtime/src/search.rs +++ b/rust/plugin_runtime/src/search.rs @@ -7,6 +7,7 @@ use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use gauntlet_common_plugin_runtime::model::JsGeneratedSearchItem; +use crate::deno::GauntletJsError; use crate::model::DenoInGeneratedSearchItem; #[op2(async)] @@ -14,7 +15,7 @@ pub async fn reload_search_index( state: Rc>, #[serde] generated_entrypoints: Vec, refresh_search_list: bool, -) -> anyhow::Result<()> { +) -> Result<(), GauntletJsError> { let api = { let state = state.borrow(); diff --git a/rust/plugin_runtime/src/ui.rs b/rust/plugin_runtime/src/ui.rs index 787b5f1..43a2165 100644 --- a/rust/plugin_runtime/src/ui.rs +++ b/rust/plugin_runtime/src/ui.rs @@ -2,6 +2,7 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; +use anyhow::anyhow; use deno_core::OpState; use deno_core::op2; use deno_core::serde_v8; @@ -17,6 +18,7 @@ use serde::Deserialize; use tokio::runtime::Handle; use crate::component_model::ComponentModel; +use crate::deno::GauntletJsError; use crate::plugin_data::PluginData; #[op2] @@ -24,7 +26,7 @@ pub fn show_plugin_error_view( state: Rc>, #[string] entrypoint_id: String, #[serde] render_location: JsUiRenderLocation, -) -> anyhow::Result<()> { +) -> Result<(), GauntletJsError> { let api = { let state = state.borrow(); @@ -47,7 +49,7 @@ pub fn show_preferences_required_view( #[string] entrypoint_id: String, plugin_preferences_required: bool, entrypoint_preferences_required: bool, -) -> anyhow::Result<()> { +) -> Result<(), GauntletJsError> { let api = { let state = state.borrow(); @@ -69,7 +71,7 @@ pub fn show_preferences_required_view( } #[op2(fast)] -pub fn clear_inline_view(state: Rc>) -> anyhow::Result<()> { +pub fn clear_inline_view(state: Rc>) -> Result<(), GauntletJsError> { let api = { let state = state.borrow(); @@ -108,12 +110,12 @@ pub fn op_react_replace_view<'a>( #[string] entrypoint_id: &str, #[string] entrypoint_name: &str, #[serde] container: serde_v8::Value<'a>, -) -> anyhow::Result<()> { +) -> Result<(), GauntletJsError> { tracing::trace!(target = "renderer_rs", "Calling op_react_replace_view..."); let mut deserializer = serde_v8::Deserializer::new(scope, container.v8_value, None); - let container = RootWidget::deserialize(&mut deserializer)?; + let container = RootWidget::deserialize(&mut deserializer).map_err(|err| anyhow!(err))?; let entrypoint_id = EntrypointId::from_string(entrypoint_id); let entrypoint_name = entrypoint_name.to_string(); @@ -139,8 +141,10 @@ pub fn op_react_replace_view<'a>( container, ) .await + .map_err(|err| anyhow!(err)) }) .await + .map_err(|err| anyhow!(err)) })??; Ok(()) @@ -162,7 +166,7 @@ pub async fn fetch_action_id_for_shortcut( modifier_control: bool, modifier_alt: bool, modifier_meta: bool, -) -> anyhow::Result> { +) -> Result, GauntletJsError> { let api = { let state = state.borrow(); @@ -186,7 +190,7 @@ pub async fn fetch_action_id_for_shortcut( } #[op2(async)] -pub async fn show_hud(state: Rc>, #[string] display: String) -> anyhow::Result<()> { +pub async fn show_hud(state: Rc>, #[string] display: String) -> Result<(), GauntletJsError> { let api = { let state = state.borrow(); @@ -199,7 +203,7 @@ pub async fn show_hud(state: Rc>, #[string] display: String) -> } #[op2(async)] -pub async fn hide_window(state: Rc>) -> anyhow::Result<()> { +pub async fn hide_window(state: Rc>) -> Result<(), GauntletJsError> { let api = { let state = state.borrow(); @@ -216,7 +220,7 @@ pub async fn update_loading_bar( state: Rc>, #[string] entrypoint_id: String, show: bool, -) -> anyhow::Result<()> { +) -> Result<(), GauntletJsError> { let api = { let state = state.borrow(); diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index 05cb58f..6bbb13f 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -6,6 +6,7 @@ edition.workspace = true # workspaces gauntlet-common.workspace = true gauntlet-utils.workspace = true +gauntlet-utils-macros.workspace = true gauntlet-client.workspace = true gauntlet-common-plugin-runtime.workspace = true gauntlet-plugin-runtime.workspace = true @@ -14,6 +15,7 @@ gauntlet-scenario-runner = { workspace = true, optional = true } # shared anyhow.workspace = true serde.workspace = true +serde_json.workspace = true tokio.workspace = true tokio-util.workspace = true tracing.workspace = true @@ -33,7 +35,8 @@ toml.workspace = true tantivy = "0.22" git2 = { version = "0.19", features = ["vendored-libgit2", "vendored-openssl"] } tempfile = "3" -sqlx = { version = "0.8", features = [ "runtime-tokio", "json", "sqlite" ] } +rusqlite = { version = "0.34.0", features = ["serde_json"] } + include_dir = "0.7" open = "5" uuid = "1.8" diff --git a/rust/server/src/plugins/data_db_repository.rs b/rust/server/src/plugins/data_db_repository.rs index 56d6916..b984263 100644 --- a/rust/server/src/plugins/data_db_repository.rs +++ b/rust/server/src/plugins/data_db_repository.rs @@ -1,57 +1,53 @@ use std::collections::HashMap; use std::collections::HashSet; +use std::sync::Arc; +use std::sync::Mutex; use anyhow::Context; use anyhow::anyhow; -use futures::StreamExt; -use futures::TryStreamExt; -use futures::future::join_all; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::PhysicalKey; use gauntlet_common::model::PhysicalShortcut; +use gauntlet_utils_macros::RusqliteFromRow; +use rusqlite::Connection; +use rusqlite::OptionalExtension; +use rusqlite::Row; +use rusqlite::named_params; use serde::Deserialize; use serde::Serialize; -use sqlx::Executor; -use sqlx::Pool; -use sqlx::Row; -use sqlx::Sqlite; -use sqlx::SqlitePool; -use sqlx::migrate::Migrator; -use sqlx::sqlite::SqliteConnectOptions; -use sqlx::types::Json; use uuid::Uuid; use crate::model::ActionShortcutKey; use crate::plugins::frecency::FrecencyItemStats; use crate::plugins::frecency::FrecencyMetaParams; -static MIGRATOR: Migrator = sqlx::migrate!("./db_migrations"); +// static MIGRATOR: Migrator = sqlx::migrate!("./db_migrations"); #[derive(Clone)] pub struct DataDbRepository { - pool: Pool, + connection: Arc>, } -#[derive(sqlx::FromRow)] +#[derive(RusqliteFromRow)] pub struct DbReadPlugin { pub id: String, pub uuid: String, pub name: String, pub description: String, pub enabled: bool, - #[sqlx(json)] + #[rusqlite(json)] pub code: DbCode, - #[sqlx(json)] + #[rusqlite(json)] pub permissions: DbPluginPermissions, - #[sqlx(rename = "type")] + #[rusqlite(rename = "type")] pub plugin_type: String, - #[sqlx(json)] + #[rusqlite(json)] pub preferences: HashMap, - #[sqlx(json)] + #[rusqlite(json)] pub preferences_user_data: HashMap, } -#[derive(sqlx::FromRow)] +#[derive(RusqliteFromRow)] pub struct DbReadPluginEntrypoint { pub id: String, pub uuid: String, @@ -60,15 +56,15 @@ pub struct DbReadPluginEntrypoint { pub description: String, pub enabled: bool, pub icon_path: Option, - #[sqlx(rename = "type")] + #[rusqlite(rename = "type")] pub entrypoint_type: String, - #[sqlx(json)] + #[rusqlite(json)] pub preferences: HashMap, - #[sqlx(json)] + #[rusqlite(json)] pub preferences_user_data: HashMap, - #[sqlx(json)] + #[rusqlite(json)] pub actions: Vec, - #[sqlx(json)] + #[rusqlite(json)] pub actions_user_data: Vec, } @@ -208,10 +204,10 @@ pub struct DbPluginActionUserData { pub modifier_meta: bool, } -#[derive(sqlx::FromRow)] +#[derive(RusqliteFromRow)] struct DbSettingsDataContainer { - // #[sqlx(json)] // https://github.com/launchbadge/sqlx/issues/2849 - pub settings: Option>, + #[rusqlite(json)] + pub settings: Option, } #[derive(Debug, Deserialize, Serialize)] @@ -367,16 +363,7 @@ pub struct DbPreferenceEnumValue { pub value: String, } -#[derive(sqlx::FromRow)] -pub struct DbReadPendingPlugin { - pub id: String, -} - -pub struct DbWritePendingPlugin { - pub id: String, -} - -#[derive(sqlx::FromRow)] +#[derive(RusqliteFromRow)] pub struct DbPluginEntrypointFrecencyStats { #[allow(unused)] pub plugin_id: String, @@ -398,307 +385,328 @@ impl DataDbRepository { std::fs::create_dir_all(&data_db_file.parent().unwrap()).context("Unable to create data directory")?; - let conn = SqliteConnectOptions::new() - .filename(data_db_file) - .create_if_missing(true); + let connection = Connection::open(&data_db_file).context("Unable to open database connection")?; - let pool = SqlitePool::connect_with(conn) - .await - .context("Unable to open database connection")?; + let db_repository = Self { + connection: Arc::new(Mutex::new(connection)), + }; - let db_repository = Self { pool }; - - let _ = db_repository.migrate_global_shortcut_to_settings().await; - - db_repository.run_migrations().await?; - - db_repository.apply_uuid_default_value().await?; - db_repository.remove_legacy_bundled_plugins().await?; + // let _ = db_repository.migrate_global_shortcut_to_settings().await; + // + // db_repository.run_migrations().await?; + // + // db_repository.apply_uuid_default_value().await?; + // db_repository.remove_legacy_bundled_plugins().await?; Ok(db_repository) } - async fn run_migrations(&self) -> anyhow::Result<()> { - // TODO backup before migration? up to 5 backups? - MIGRATOR - .run(&self.pool) - .await - .context("Unable apply database migration")?; + // async fn run_migrations(&self) -> anyhow::Result<()> { + // MIGRATOR + // .run(&self.connection) + // .await + // .context("Unable apply database migration")?; + // + // Ok(()) + // } + // + // async fn apply_uuid_default_value(&self) -> anyhow::Result<()> { + // // language=SQLite + // let mut stream = self + // .connection + // .fetch(sqlx::query("SELECT id FROM plugin WHERE uuid IS NULL")); + // while let Some(row) = stream.next().await { + // let row = row?; + // let id: &str = row.get("id"); + // + // // language=SQLite + // sqlx::query("UPDATE plugin SET uuid = ?1 WHERE id = ?2") + // .bind(Uuid::new_v4().to_string()) + // .bind(id) + // .execute(&self.connection) + // .await?; + // } + // + // // language=SQLite + // let mut stream = self + // .connection + // .fetch(sqlx::query("SELECT id FROM plugin_entrypoint WHERE uuid IS NULL")); + // while let Some(row) = stream.next().await { + // let row = row?; + // let id: &str = row.get("id"); + // + // // language=SQLite + // sqlx::query("UPDATE plugin_entrypoint SET uuid = ?1 WHERE id = ?2") + // .bind(Uuid::new_v4().to_string()) + // .bind(id) + // .execute(&self.connection) + // .await?; + // } + // + // Ok(()) + // } + // + // async fn remove_legacy_bundled_plugins(&self) -> anyhow::Result<()> { + // self.remove_plugin("builtin://applications").await?; + // self.remove_plugin("builtin://calculator").await?; + // self.remove_plugin("builtin://settings").await?; + // + // Ok(()) + // } + // + // async fn migrate_global_shortcut_to_settings(&self) -> anyhow::Result<()> { + // #[derive(RusqliteModel)] + // struct DbSettingsDataOldContainer { + // #[rusqlite(json)] + // pub global_shortcut: DbSettingsGlobalShortcutOldData, + // pub settings: Option>, + // } + // + // #[derive(Debug, Deserialize, Serialize)] + // pub struct DbSettingsGlobalShortcutOldData { + // pub physical_key: String, + // pub modifier_shift: bool, + // pub modifier_control: bool, + // pub modifier_alt: bool, + // pub modifier_meta: bool, + // #[serde(default)] + // pub unset: bool, + // #[serde(default)] + // pub error: Option, + // } + // + // // language=SQLite + // let mut data = sqlx::query_as::<_, DbSettingsDataOldContainer>("SELECT * FROM settings_data") + // .fetch_one(&self.connection) + // .await?; + // + // let shortcut_data = &data.global_shortcut; + // + // let shortcut = if shortcut_data.unset { + // None + // } else { + // Some(DbSettingsGlobalShortcutData { + // shortcut: DbSettingsShortcut { + // physical_key: shortcut_data.physical_key.clone(), + // modifier_shift: shortcut_data.modifier_shift, + // modifier_control: shortcut_data.modifier_control, + // modifier_alt: shortcut_data.modifier_alt, + // modifier_meta: shortcut_data.modifier_meta, + // }, + // error: None, + // }) + // }; + // + // if let Some(settings) = &mut data.settings { + // settings.global_shortcut = shortcut; + // } + // + // // language=SQLite + // let sql = r#" + // INSERT INTO settings_data (id, global_shortcut, settings) + // VALUES(?1, ?2, ?3) + // ON CONFLICT (id) + // DO UPDATE SET settings = ?3 + // "#; + // + // sqlx::query(sql) + // .bind(SETTINGS_DATA_ID) + // .bind(Json(data.global_shortcut)) + // .bind(data.settings) + // .execute(&self.connection) + // .await?; + // + // Ok(()) + // } - Ok(()) - } - - async fn apply_uuid_default_value(&self) -> anyhow::Result<()> { + pub fn list_plugins(&self) -> anyhow::Result> { // language=SQLite - let mut stream = self.pool.fetch(sqlx::query("SELECT id FROM plugin WHERE uuid IS NULL")); - while let Some(row) = stream.next().await { - let row = row?; - let id: &str = row.get("id"); + let query = "SELECT * FROM plugin"; - // language=SQLite - sqlx::query("UPDATE plugin SET uuid = ?1 WHERE id = ?2") - .bind(Uuid::new_v4().to_string()) - .bind(id) - .execute(&self.pool) - .await?; - } + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; - // language=SQLite - let mut stream = self - .pool - .fetch(sqlx::query("SELECT id FROM plugin_entrypoint WHERE uuid IS NULL")); - while let Some(row) = stream.next().await { - let row = row?; - let id: &str = row.get("id"); - - // language=SQLite - sqlx::query("UPDATE plugin_entrypoint SET uuid = ?1 WHERE id = ?2") - .bind(Uuid::new_v4().to_string()) - .bind(id) - .execute(&self.pool) - .await?; - } - - Ok(()) - } - - async fn remove_legacy_bundled_plugins(&self) -> anyhow::Result<()> { - self.remove_plugin("builtin://applications").await?; - self.remove_plugin("builtin://calculator").await?; - self.remove_plugin("builtin://settings").await?; - - Ok(()) - } - - async fn migrate_global_shortcut_to_settings(&self) -> anyhow::Result<()> { - #[derive(sqlx::FromRow)] - struct DbSettingsDataOldContainer { - #[sqlx(json)] - pub global_shortcut: DbSettingsGlobalShortcutOldData, - pub settings: Option>, - } - - #[derive(Debug, Deserialize, Serialize)] - pub struct DbSettingsGlobalShortcutOldData { - pub physical_key: String, - pub modifier_shift: bool, - pub modifier_control: bool, - pub modifier_alt: bool, - pub modifier_meta: bool, - #[serde(default)] - pub unset: bool, - #[serde(default)] - pub error: Option, - } - - // language=SQLite - let mut data = sqlx::query_as::<_, DbSettingsDataOldContainer>("SELECT * FROM settings_data") - .fetch_one(&self.pool) - .await?; - - let shortcut_data = &data.global_shortcut; - - let shortcut = if shortcut_data.unset { - None - } else { - Some(DbSettingsGlobalShortcutData { - shortcut: DbSettingsShortcut { - physical_key: shortcut_data.physical_key.clone(), - modifier_shift: shortcut_data.modifier_shift, - modifier_control: shortcut_data.modifier_control, - modifier_alt: shortcut_data.modifier_alt, - modifier_meta: shortcut_data.modifier_meta, - }, - error: None, - }) - }; - - if let Some(settings) = &mut data.settings { - settings.global_shortcut = shortcut; - } - - // language=SQLite - let sql = r#" - INSERT INTO settings_data (id, global_shortcut, settings) - VALUES(?1, ?2, ?3) - ON CONFLICT (id) - DO UPDATE SET settings = ?3 - "#; - - sqlx::query(sql) - .bind(SETTINGS_DATA_ID) - .bind(Json(data.global_shortcut)) - .bind(data.settings) - .execute(&self.pool) - .await?; - - Ok(()) - } - - pub async fn list_plugins(&self) -> anyhow::Result> { - // language=SQLite - let plugins = sqlx::query_as::<_, DbReadPlugin>("SELECT * FROM plugin") - .fetch_all(&self.pool) - .await?; + let plugins = connection + .prepare(query)? + .query_map([], DbReadPlugin::from_row)? + .collect::, _>>()?; Ok(plugins) } - pub async fn list_plugins_and_entrypoints( - &self, - ) -> anyhow::Result)>> { + pub fn list_plugins_and_entrypoints(&self) -> anyhow::Result)>> { // language=SQLite - let plugins = self.list_plugins().await?; + let plugins = self.list_plugins()?; - let result = futures::stream::iter(plugins) - .then(|plugin| { - async move { - let entrypoints = self.get_entrypoints_by_plugin_id(&plugin.id).await?; + let result = plugins + .into_iter() + .map(|plugin| { + let entrypoints = self.get_entrypoints_by_plugin_id(&plugin.id)?; - Ok::<(DbReadPlugin, Vec), anyhow::Error>((plugin, entrypoints)) - } + Ok::<(DbReadPlugin, Vec), anyhow::Error>((plugin, entrypoints)) }) - .try_collect::)>>() - .await?; + .collect::)>, _>>()?; Ok(result) } - pub async fn get_plugin_by_id(&self, plugin_id: &str) -> anyhow::Result { - self.get_plugin_by_id_with_executor(plugin_id, &self.pool).await + pub fn get_plugin_by_id(&self, plugin_id: &str) -> anyhow::Result { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + self.get_plugin_by_id_with_executor(plugin_id, &connection) } - async fn get_plugin_by_id_with_executor<'a, E>(&self, plugin_id: &str, executor: E) -> anyhow::Result - where - E: Executor<'a, Database = Sqlite>, - { + fn get_plugin_by_id_with_executor(&self, plugin_id: &str, connection: &Connection) -> anyhow::Result { // language=SQLite - let result = sqlx::query_as::<_, DbReadPlugin>("SELECT * FROM plugin WHERE id = ?1") - .bind(plugin_id) - .fetch_one(executor) - .await?; + let query = "SELECT * FROM plugin WHERE id = :id"; + + // todo change into query_one when updating to rusqlite 0.36 + let result = connection.query_row( + query, + named_params! { + ":id": plugin_id + }, + DbReadPlugin::from_row, + )?; Ok(result) } - pub async fn get_plugin_by_id_option(&self, plugin_id: &str) -> anyhow::Result> { - self.get_plugin_by_id_option_with_executor(plugin_id, &self.pool).await + pub fn get_plugin_by_id_option(&self, plugin_id: &str) -> anyhow::Result> { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + self.get_plugin_by_id_option_with_executor(plugin_id, &connection) } - async fn get_plugin_by_id_option_with_executor<'a, E>( + fn get_plugin_by_id_option_with_executor( &self, plugin_id: &str, - executor: E, - ) -> anyhow::Result> - where - E: Executor<'a, Database = Sqlite>, - { + connection: &Connection, + ) -> anyhow::Result> { // language=SQLite - let result = sqlx::query_as::<_, DbReadPlugin>("SELECT * FROM plugin WHERE id = ?1") - .bind(plugin_id) - .fetch_optional(executor) - .await?; + let query = "SELECT * FROM plugin WHERE id = :id"; + + let result = connection + .query_row( + query, + named_params! { + ":id": plugin_id + }, + DbReadPlugin::from_row, + ) + .optional()?; Ok(result) } - pub async fn get_entrypoints_by_plugin_id(&self, plugin_id: &str) -> anyhow::Result> { - self.get_entrypoints_by_plugin_id_with_executor(plugin_id, &self.pool) - .await + pub fn get_entrypoints_by_plugin_id(&self, plugin_id: &str) -> anyhow::Result> { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + self.get_entrypoints_by_plugin_id_with_executor(plugin_id, &connection) } - async fn get_entrypoints_by_plugin_id_with_executor<'a, E>( + fn get_entrypoints_by_plugin_id_with_executor( &self, plugin_id: &str, - executor: E, - ) -> anyhow::Result> - where - E: Executor<'a, Database = Sqlite>, - { + connection: &Connection, + ) -> anyhow::Result> { // language=SQLite - let result = - sqlx::query_as::<_, DbReadPluginEntrypoint>("SELECT * FROM plugin_entrypoint WHERE plugin_id = ?1") - .bind(plugin_id) - .fetch_all(executor) - .await?; + let query = "SELECT * FROM plugin_entrypoint WHERE plugin_id = :id"; + + let result = connection + .prepare(query)? + .query_and_then( + named_params! { + ":id": plugin_id + }, + DbReadPluginEntrypoint::from_row, + )? + .collect::, _>>()?; Ok(result) } - pub async fn get_entrypoint_by_id( + pub fn get_entrypoint_by_id(&self, plugin_id: &str, entrypoint_id: &str) -> anyhow::Result { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + self.get_entrypoint_by_id_with_executor(plugin_id, entrypoint_id, &connection) + } + + fn get_entrypoint_by_id_with_executor( &self, plugin_id: &str, entrypoint_id: &str, + connection: &Connection, ) -> anyhow::Result { - self.get_entrypoint_by_id_with_executor(plugin_id, entrypoint_id, &self.pool) - .await - } - - async fn get_entrypoint_by_id_with_executor<'a, E>( - &self, - plugin_id: &str, - entrypoint_id: &str, - executor: E, - ) -> anyhow::Result - where - E: Executor<'a, Database = Sqlite>, - { // language=SQLite - let result = sqlx::query_as::<_, DbReadPluginEntrypoint>( - "SELECT * FROM plugin_entrypoint WHERE id = ?1 AND plugin_id = ?2", - ) - .bind(entrypoint_id) - .bind(plugin_id) - .fetch_one(executor) - .await?; + let query = "SELECT * FROM plugin_entrypoint WHERE id = :id AND plugin_id = :plugin_id"; + + // todo change into query_one when updating to rusqlite 0.36 + let result = connection.query_row( + query, + named_params! { + ":id": entrypoint_id, + ":plugin_id": plugin_id, + }, + DbReadPluginEntrypoint::from_row, + )?; Ok(result) } - pub async fn get_entrypoint_by_id_option( + pub fn get_entrypoint_by_id_option( &self, plugin_id: &str, entrypoint_id: &str, ) -> anyhow::Result> { - self.get_entrypoint_by_id_option_with_executor(plugin_id, entrypoint_id, &self.pool) - .await + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + self.get_entrypoint_by_id_option_with_executor(plugin_id, entrypoint_id, &connection) } - async fn get_entrypoint_by_id_option_with_executor<'a, E>( + fn get_entrypoint_by_id_option_with_executor( &self, plugin_id: &str, entrypoint_id: &str, - executor: E, - ) -> anyhow::Result> - where - E: Executor<'a, Database = Sqlite>, - { + connection: &Connection, + ) -> anyhow::Result> { // language=SQLite - let result = sqlx::query_as::<_, DbReadPluginEntrypoint>( - "SELECT * FROM plugin_entrypoint WHERE id = ?1 AND plugin_id = ?2", - ) - .bind(entrypoint_id) - .bind(plugin_id) - .fetch_optional(executor) - .await?; + let query = "SELECT * FROM plugin_entrypoint WHERE id = :id AND plugin_id = :plugin_id"; + + let result = connection + .query_row( + query, + named_params! { + ":id": entrypoint_id, + ":plugin_id": plugin_id, + }, + DbReadPluginEntrypoint::from_row, + ) + .optional()?; Ok(result) } - pub async fn get_inline_view_entrypoint_id_for_plugin(&self, plugin_id: &str) -> anyhow::Result> { - // language=SQLite - let entrypoint_id = sqlx::query_as::<_, (String,)>( - "SELECT id FROM plugin_entrypoint WHERE plugin_id = ?1 AND type = 'inline-view'", - ) - .bind(plugin_id) - .fetch_optional(&self.pool) - .await? - .map(|result| result.0); + pub fn get_inline_view_entrypoint_id_for_plugin(&self, plugin_id: &str) -> anyhow::Result> { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; - Ok(entrypoint_id) + // language=SQLite + let query = "SELECT id FROM plugin_entrypoint WHERE plugin_id = :plugin_id AND type = 'inline-view'"; + + #[derive(RusqliteFromRow)] + struct DbReadInlineViewEntrypoint { + pub id: String, + } + + let result = connection + .query_row( + query, + named_params! { + ":plugin_id": plugin_id + }, + DbReadInlineViewEntrypoint::from_row, + ) + .optional()? + .map(|row| row.id); + + Ok(result) } - pub async fn action_shortcuts( + pub fn action_shortcuts( &self, plugin_id: &str, entrypoint_id: &str, @@ -707,7 +715,7 @@ impl DataDbRepository { actions, actions_user_data, .. - } = self.get_entrypoint_by_id(plugin_id, entrypoint_id).await?; + } = self.get_entrypoint_by_id(plugin_id, entrypoint_id)?; let actions_user_data: HashMap<_, _> = actions_user_data .into_iter() @@ -778,7 +786,7 @@ impl DataDbRepository { Ok(action_shortcuts) } - pub async fn get_action_id_for_shortcut( + pub fn get_action_id_for_shortcut( &self, plugin_id: &str, entrypoint_id: &str, @@ -788,20 +796,42 @@ impl DataDbRepository { modifier_alt: bool, modifier_meta: bool, ) -> anyhow::Result> { - // language=SQLite - let sql = r#"SELECT json_each.value ->> 'id' FROM plugin_entrypoint e, json_each(actions_user_data) WHERE e.plugin_id = ?1 AND e.id = ?2 AND json_each.value ->> 'key' = ?3 AND json_each.value ->> 'modifier_shift' = ?4 AND json_each.value ->> 'modifier_control' = ?5 AND json_each.value ->> 'modifier_alt' = ?6 AND json_each.value ->> 'modifier_meta' = ?6"#; + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; - let action_id = sqlx::query_as::<_, (String,)>(sql) - .bind(plugin_id) - .bind(entrypoint_id) - .bind(key.to_value()) - .bind(modifier_shift) - .bind(modifier_control) - .bind(modifier_alt) - .bind(modifier_meta) - .fetch_optional(&self.pool) - .await? - .map(|result| result.0); + // language=SQLite + let query = r#" + SELECT json_each.value ->> 'id' AS id + FROM plugin_entrypoint e, json_each(actions_user_data) + WHERE e.plugin_id = :plugin_id + AND e.id = :entrypoint_id + AND json_each.value ->> 'key' = :key + AND json_each.value ->> 'modifier_shift' = :modifier_shift + AND json_each.value ->> 'modifier_control' = :modifier_control + AND json_each.value ->> 'modifier_alt' = :modifier_alt + AND json_each.value ->> 'modifier_meta' = :modifier_meta + "#; + + #[derive(RusqliteFromRow)] + struct DbReadActionId { + pub id: String, + } + + let action_id = connection + .query_row( + query, + named_params! { + ":plugin_id": plugin_id, + ":entrypoint_id": entrypoint_id, + ":key": key.to_value(), + ":modifier_shift": modifier_shift, + ":modifier_control": modifier_control, + ":modifier_alt": modifier_alt, + ":modifier_meta": modifier_meta, + }, + DbReadActionId::from_row, + ) + .optional()? + .map(|row| row.id); match action_id { Some(action_id) => Ok(Some(action_id)), @@ -826,143 +856,155 @@ impl DataDbRepository { }; // language=SQLite - let sql = r#"SELECT json_each.value ->> 'id' FROM plugin_entrypoint e, json_each(actions) WHERE e.plugin_id = ?1 AND e.id = ?2 AND json_each.value ->> 'key' = ?3 AND json_each.value ->> 'kind' = ?4"#; + let query = r#" + SELECT json_each.value ->> 'id' AS id + FROM plugin_entrypoint e, json_each(actions) + WHERE e.plugin_id = :plugin_id + AND e.id = :entrypoint_id + AND json_each.value ->> 'key' = :key + AND json_each.value ->> 'kind' = :kind + "#; let Some(logical_key) = ActionShortcutKey::from_physical_key(key, modifier_shift) else { return Ok(None); }; - let action_id = sqlx::query_as::<_, (String,)>(sql) - .bind(plugin_id) - .bind(entrypoint_id) - .bind(logical_key.to_value()) - .bind(&kind) - .fetch_optional(&self.pool) - .await? - .map(|result| result.0); + let action_id = connection + .query_row( + query, + named_params! { + ":plugin_id": plugin_id, + ":entrypoint_id": entrypoint_id, + ":key": logical_key.to_value(), + ":kind": kind, + }, + DbReadActionId::from_row, + ) + .optional()? + .map(|row| row.id); Ok(action_id) } } } - pub async fn list_pending_plugins(&self) -> anyhow::Result> { - // language=SQLite - let plugins = sqlx::query_as::<_, DbReadPendingPlugin>("SELECT * FROM pending_plugin") - .fetch_all(&self.pool) - .await?; + pub fn is_plugin_enabled(&self, plugin_id: &str) -> anyhow::Result { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; - Ok(plugins) - } - - pub async fn is_plugin_pending(&self, plugin_id: &str) -> anyhow::Result { - // language=SQLite - let result = sqlx::query_as::<_, (u8,)>("SELECT 1 FROM pending_plugin WHERE id = ?1") - .bind(plugin_id) - .fetch_optional(&self.pool) - .await?; - - Ok(result.is_some()) - } - - pub async fn does_plugin_exist(&self, plugin_id: &str) -> anyhow::Result { - // language=SQLite - let result = sqlx::query_as::<_, (u8,)>("SELECT 1 FROM plugin WHERE id = ?1") - .bind(plugin_id) - .fetch_optional(&self.pool) - .await?; - - Ok(result.is_some()) - } - - pub async fn is_plugin_enabled(&self, plugin_id: &str) -> anyhow::Result { - #[derive(sqlx::FromRow)] + #[derive(RusqliteFromRow)] struct DbReadPluginEnabled { pub enabled: bool, } // language=SQLite - let result = sqlx::query_as::<_, DbReadPluginEnabled>("SELECT enabled FROM plugin WHERE id = ?1") - .bind(plugin_id) - .fetch_one(&self.pool) - .await?; + let query = "SELECT enabled FROM plugin WHERE id = :id"; + + // todo change into query_one when updating to rusqlite 0.36 + let result = connection.query_row( + query, + named_params! { + ":id": plugin_id + }, + DbReadPluginEnabled::from_row, + )?; Ok(result.enabled) } - pub async fn get_asset_data(&self, plugin_id: &str, path: &str) -> anyhow::Result> { - #[derive(sqlx::FromRow)] + pub fn get_asset_data(&self, plugin_id: &str, path: &str) -> anyhow::Result> { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + + #[derive(RusqliteFromRow)] struct DbReadPluginAssetData { pub data: Vec, } // language=SQLite - let result = sqlx::query_as::<_, DbReadPluginAssetData>( - "SELECT data FROM plugin_asset_data WHERE plugin_id = ?1 and path = ?2", - ) - .bind(plugin_id) - .bind(path) - .fetch_one(&self.pool) - .await?; + let query = "SELECT data FROM plugin_asset_data WHERE plugin_id = :plugin_id and path = :path"; + + // todo change into query_one when updating to rusqlite 0.36 + let result = connection.query_row( + query, + named_params! { + ":plugin_id": plugin_id, + ":path": path, + }, + DbReadPluginAssetData::from_row, + )?; Ok(result.data) } - async fn get_all_asset_data_paths<'a, E>(&self, plugin_id: &str, executor: E) -> anyhow::Result> - where - E: Executor<'a, Database = Sqlite>, - { + fn get_all_asset_data_paths(&self, plugin_id: &str, connection: &Connection) -> anyhow::Result> { + #[derive(RusqliteFromRow)] + struct DbReadPluginAssetPaths { + pub path: String, + } + // language=SQLite - let result = sqlx::query_as::<_, (String,)>("SELECT path FROM plugin_asset_data WHERE plugin_id = ?1") - .bind(plugin_id) - .fetch_all(executor) - .await? + let query = "SELECT path FROM plugin_asset_data WHERE plugin_id = :plugin_id"; + + let result = connection + .prepare(query)? + .query_and_then( + named_params! { + ":plugin_id": plugin_id + }, + DbReadPluginAssetPaths::from_row, + )? + .collect::, _>>()? .into_iter() - .map(|result| result.0) + .map(|row| row.path) .collect(); Ok(result) } - pub async fn inline_view_shortcuts(&self) -> anyhow::Result>> { + pub fn inline_view_shortcuts(&self) -> anyhow::Result>> { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + + #[derive(RusqliteFromRow)] + struct DbReadPluginInlineViewShortcuts { + pub id: String, + pub plugin_id: String, + } + // language=SQLite - let shortcuts: Vec<_> = sqlx::query_as::<_, (String, String)>( - "SELECT id, plugin_id FROM plugin_entrypoint WHERE type = 'inline-view'", - ) - .fetch_all(&self.pool) - .await? - .into_iter() - .map(|(entrypoint_id, plugin_id)| { - async move { - let shortcuts = self.action_shortcuts(&plugin_id, &entrypoint_id).await?; + let query = "SELECT id, plugin_id FROM plugin_entrypoint WHERE type = 'inline-view'"; - Ok((plugin_id, shortcuts)) - } - }) - .collect(); + let result = connection + .prepare(query)? + .query_and_then([], DbReadPluginInlineViewShortcuts::from_row)? + .collect::, _>>()? + .into_iter() + .map(|row| { + let shortcuts = self.action_shortcuts(&row.plugin_id, &row.id)?; - join_all(shortcuts).await.into_iter().collect() + Ok::<_, anyhow::Error>((row.plugin_id, shortcuts)) + }) + .collect::, _>>()?; + + Ok(result) } - pub async fn mark_entrypoint_frecency(&self, plugin_id: &str, entrypoint_id: &str) -> anyhow::Result<()> { - let mut tx = self.pool.begin().await?; + pub fn mark_entrypoint_frecency(&self, plugin_id: &str, entrypoint_id: &str) -> anyhow::Result<()> { + let mut connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + let tx = connection.transaction()?; // TODO reset time after 5 half lives // https://github.com/camdencheek/fre/blob/6574ee7045061957de24855567e0abf05f2778d9/src/main.rs#L23 // why? dunno - #[derive(sqlx::FromRow)] + #[derive(RusqliteFromRow)] struct DbFrecencyMetaParams { pub reference_time: f64, pub half_life: f64, } // language=SQLite - let meta_params = sqlx::query_as::<_, DbFrecencyMetaParams>( - "SELECT reference_time, half_life FROM plugin_entrypoint_frecency_stats", - ) - .fetch_optional(&mut *tx) - .await?; + let query = "SELECT reference_time, half_life FROM plugin_entrypoint_frecency_stats"; + + let meta_params = tx.query_row(query, [], DbFrecencyMetaParams::from_row).optional()?; let meta_params = match meta_params { None => FrecencyMetaParams::default(), @@ -975,11 +1017,23 @@ impl DataDbRepository { }; // language=SQLite - let stats = sqlx::query_as::<_, DbPluginEntrypointFrecencyStats>("SELECT plugin_id, entrypoint_id, reference_time, half_life, last_accessed, frecency, num_accesses FROM plugin_entrypoint_frecency_stats WHERE plugin_id = ?1 and entrypoint_id = ?2") - .bind(plugin_id) - .bind(entrypoint_id) - .fetch_optional(&mut *tx) - .await?; + let query = r#" + SELECT plugin_id, entrypoint_id, reference_time, half_life, last_accessed, frecency, num_accesses + FROM plugin_entrypoint_frecency_stats + WHERE plugin_id = :plugin_id + and entrypoint_id = :entrypoint_id + "#; + + let stats = tx + .query_row( + query, + named_params! { + ":plugin_id": plugin_id, + ":entrypoint_id": entrypoint_id, + }, + DbPluginEntrypointFrecencyStats::from_row, + ) + .optional()?; let mut new_stats = match stats { None => FrecencyItemStats::new(meta_params.reference_time, meta_params.half_life), @@ -997,199 +1051,257 @@ impl DataDbRepository { new_stats.mark_used(); // language=SQLite - let sql = r#" + let query = r#" INSERT OR REPLACE INTO plugin_entrypoint_frecency_stats (plugin_id, entrypoint_id, reference_time, half_life, last_accessed, frecency, num_accesses) - VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7) + VALUES( + :plugin_id, + :entrypoint_id, + :reference_time, + :half_life, + :last_accessed, + :frecency, + :num_accesses + ) "#; - sqlx::query(sql) - .bind(plugin_id) - .bind(entrypoint_id) - .bind(new_stats.reference_time) - .bind(new_stats.half_life) - .bind(new_stats.last_accessed) - .bind(new_stats.frecency) - .bind(new_stats.num_accesses) - .execute(&mut *tx) - .await?; + tx.execute( + query, + named_params! { + ":plugin_id": plugin_id, + ":entrypoint_id": entrypoint_id, + ":reference_time": new_stats.reference_time, + ":half_life": new_stats.half_life, + ":last_accessed": new_stats.last_accessed, + ":frecency": new_stats.frecency, + ":num_accesses": new_stats.num_accesses + }, + )?; - tx.commit().await?; + tx.commit()?; Ok(()) } - pub async fn get_frecency_for_plugin(&self, plugin_id: &str) -> anyhow::Result> { + pub fn get_frecency_for_plugin(&self, plugin_id: &str) -> anyhow::Result> { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + + #[derive(RusqliteFromRow)] + struct DbFrecencyStats { + pub entrypoint_id: String, + pub frecency: f64, + } + // language=SQLite - let result = sqlx::query_as::<_, (String, f64)>( - "SELECT entrypoint_id, frecency FROM plugin_entrypoint_frecency_stats WHERE plugin_id = ?1", - ) - .bind(plugin_id) - .fetch_all(&self.pool) - .await? - .into_iter() - .collect(); + let query = "SELECT entrypoint_id, frecency FROM plugin_entrypoint_frecency_stats WHERE plugin_id = :plugin_id"; + + let result = connection + .prepare(query)? + .query_and_then( + named_params! { + ":plugin_id": plugin_id + }, + DbFrecencyStats::from_row, + )? + .collect::, _>>()? + .into_iter() + .map(|row| (row.entrypoint_id, row.frecency)) + .collect::>(); Ok(result) } - pub async fn set_plugin_enabled(&self, plugin_id: &str, enabled: bool) -> anyhow::Result<()> { + pub fn set_plugin_enabled(&self, plugin_id: &str, enabled: bool) -> anyhow::Result<()> { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + // language=SQLite - sqlx::query("UPDATE plugin SET enabled = ?1 WHERE id = ?2") - .bind(enabled) - .bind(plugin_id) - .execute(&self.pool) - .await?; + let query = "UPDATE plugin SET enabled = :enabled WHERE id = :id"; + + connection.execute( + query, + named_params! { + ":id": plugin_id, + ":enabled": enabled, + }, + )?; Ok(()) } - pub async fn set_plugin_entrypoint_enabled( + pub fn set_plugin_entrypoint_enabled( &self, plugin_id: &str, entrypoint_id: &str, enabled: bool, ) -> anyhow::Result<()> { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + // language=SQLite - sqlx::query("UPDATE plugin_entrypoint SET enabled = ?1 WHERE id = ?2 AND plugin_id = ?3") - .bind(enabled) - .bind(entrypoint_id) - .bind(plugin_id) - .execute(&self.pool) - .await?; + let query = "UPDATE plugin_entrypoint SET enabled = :enabled WHERE id = :id AND plugin_id = :plugin_id"; + + connection.execute( + query, + named_params! { + ":id": entrypoint_id, + ":plugin_id": plugin_id, + ":enabled": enabled + }, + )?; Ok(()) } - pub async fn get_settings(&self) -> anyhow::Result { + pub fn get_settings(&self) -> anyhow::Result { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + // language=SQLite - let settings = sqlx::query_as::<_, DbSettingsDataContainer>("SELECT settings FROM settings_data") - .fetch_optional(&self.pool) - .await?; + let query = "SELECT settings FROM settings_data"; + + let settings = connection + .query_row(query, [], DbSettingsDataContainer::from_row) + .optional()?; let theme = settings.map(|data| data.settings).flatten().unwrap_or_default(); - Ok(theme.0) + Ok(theme) } - pub async fn set_settings(&self, value: DbSettings) -> anyhow::Result<()> { + pub fn set_settings(&self, value: DbSettings) -> anyhow::Result<()> { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + // language=SQLite - let sql = r#" + let query = r#" INSERT INTO settings_data (id, settings) - VALUES(?1, ?2) + VALUES(:id, :settings) ON CONFLICT (id) - DO UPDATE SET settings = ?2 + DO UPDATE SET settings = :settings "#; - sqlx::query(sql) - .bind(SETTINGS_DATA_ID) - .bind(Json(value)) - .execute(&self.pool) - .await?; + connection.execute( + query, + named_params! { + ":id": SETTINGS_DATA_ID, + ":settings": serde_json::to_value(value)?, + }, + )?; Ok(()) } - pub async fn set_preference_value( + pub fn set_preference_value( &self, plugin_id: String, entrypoint_id: Option, preference_id: String, value: DbPluginPreferenceUserData, ) -> anyhow::Result<()> { - let mut tx = self.pool.begin().await?; + let mut connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + let mut tx = connection.transaction()?; match entrypoint_id { None => { let mut user_data = self - .get_plugin_by_id_with_executor(&plugin_id, &mut *tx) - .await? + .get_plugin_by_id_with_executor(&plugin_id, &mut tx)? .preferences_user_data; user_data.insert(preference_id, value); // language=SQLite - sqlx::query("UPDATE plugin SET preferences_user_data = ?1 WHERE id = ?2") - .bind(Json(user_data)) - .bind(&plugin_id) - .execute(&mut *tx) - .await?; + let query = "UPDATE plugin SET preferences_user_data = :data WHERE id = :id"; + + tx.execute( + query, + named_params! { + ":data": serde_json::to_value(user_data)?, + ":id": plugin_id + }, + )?; } Some(entrypoint_id) => { let mut user_data = self - .get_entrypoint_by_id_with_executor(&plugin_id, &entrypoint_id, &mut *tx) - .await? + .get_entrypoint_by_id_with_executor(&plugin_id, &entrypoint_id, &mut tx)? .preferences_user_data; user_data.insert(preference_id, value); // language=SQLite - sqlx::query("UPDATE plugin_entrypoint SET preferences_user_data = ?1 WHERE id = ?2 AND plugin_id = ?3") - .bind(Json(user_data)) - .bind(&entrypoint_id) - .bind(&plugin_id) - .execute(&mut *tx) - .await?; + let query = "UPDATE plugin_entrypoint SET preferences_user_data = :data WHERE id = :id AND plugin_id = :plugin_id"; + + tx.execute( + query, + named_params! { + ":data": serde_json::to_value(user_data)?, + ":id": entrypoint_id, + ":plugin_id": plugin_id, + }, + )?; } } - tx.commit().await?; + tx.commit()?; Ok(()) } - pub async fn save_pending_plugin(&self, plugin: DbWritePendingPlugin) -> anyhow::Result<()> { + pub fn remove_plugin(&self, plugin_id: &str) -> anyhow::Result<()> { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + // language=SQLite - sqlx::query("INSERT INTO pending_plugin VALUES(?1)") - .bind(&plugin.id) - .execute(&self.pool) - .await?; + let query = "DELETE FROM plugin WHERE id = :id"; + + connection.execute( + query, + named_params! { + ":id": plugin_id + }, + )?; Ok(()) } - pub async fn remove_plugin(&self, plugin_id: &str) -> anyhow::Result<()> { - // language=SQLite - sqlx::query("DELETE FROM plugin WHERE id = ?1") - .bind(plugin_id) - .execute(&self.pool) - .await?; - Ok(()) - } - - pub async fn save_plugin(&self, new_plugin: DbWritePlugin) -> anyhow::Result<()> { - let mut tx = self.pool.begin().await?; + pub fn save_plugin(&self, new_plugin: DbWritePlugin) -> anyhow::Result<()> { + let mut connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + let mut tx = connection.transaction()?; let (uuid, enabled, preferences_user_data) = self - .get_plugin_by_id_option_with_executor(&new_plugin.id, &mut *tx) - .await? + .get_plugin_by_id_option_with_executor(&new_plugin.id, &mut tx)? .map(|plugin| (plugin.uuid, plugin.enabled, plugin.preferences_user_data)) .unwrap_or((Uuid::new_v4().to_string(), new_plugin.enabled, HashMap::new())); // language=SQLite - let sql = r#" + let query = r#" INSERT INTO plugin (id, name, enabled, code, permissions, preferences, preferences_user_data, description, type, uuid) - VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10) + VALUES(:id, :name, :enabled, :code, :permissions, :preferences, :preferences_user_data, :description, :type, :uuid) ON CONFLICT (id) - DO UPDATE SET name = ?2, enabled = ?3, code = ?4, permissions = ?5, preferences = ?6, preferences_user_data = ?7, description = ?8, type = ?9, uuid = ?10 + DO UPDATE SET + name = :name, + enabled = :enabled, + code = :code, + permissions = :permissions, + preferences = :preferences, + preferences_user_data = :preferences_user_data , + description = :description , + type = :type, + uuid = :uuid "#; - sqlx::query(sql) - .bind(&new_plugin.id) - .bind(new_plugin.name) - .bind(enabled) - .bind(Json(new_plugin.code)) - .bind(Json(new_plugin.permissions)) - .bind(Json(new_plugin.preferences)) - .bind(Json(preferences_user_data)) - .bind(new_plugin.description) - .bind(new_plugin.plugin_type) - .bind(uuid) - .execute(&mut *tx) - .await?; + tx.execute( + query, + named_params! { + ":id": new_plugin.id, + ":name": new_plugin.name, + ":enabled": enabled, + ":code": serde_json::to_value(&new_plugin.code)?, + ":permissions": serde_json::to_value(&new_plugin.permissions)?, + ":preferences": serde_json::to_value(&new_plugin.preferences)?, + ":preferences_user_data": serde_json::to_value(&preferences_user_data)?, + ":description": new_plugin.description, + ":type": new_plugin.plugin_type, + ":uuid": uuid + }, + )?; let mut old_entrypoint_ids = self - .get_entrypoints_by_plugin_id_with_executor(&new_plugin.id, &mut *tx) - .await? + .get_entrypoints_by_plugin_id_with_executor(&new_plugin.id, &mut tx)? .into_iter() .map(|entrypoint| entrypoint.id) .collect::>(); @@ -1198,8 +1310,7 @@ impl DataDbRepository { old_entrypoint_ids.remove(&new_entrypoint.id); let (uuid, preferences_user_data, actions_user_data, enabled) = self - .get_entrypoint_by_id_option_with_executor(&new_plugin.id, &new_entrypoint.id, &mut *tx) - .await? + .get_entrypoint_by_id_option_with_executor(&new_plugin.id, &new_entrypoint.id, &mut tx)? .map(|entrypoint| { ( entrypoint.uuid, @@ -1211,55 +1322,90 @@ impl DataDbRepository { .unwrap_or((Uuid::new_v4().to_string(), HashMap::new(), vec![], true)); // language=SQLite - sqlx::query("INSERT OR REPLACE INTO plugin_entrypoint (id, plugin_id, name, enabled, type, preferences, preferences_user_data, description, actions, actions_user_data, icon_path, uuid) VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)") - .bind(&new_entrypoint.id) - .bind(&new_plugin.id) - .bind(new_entrypoint.name) - .bind(enabled) - .bind(new_entrypoint.entrypoint_type) - .bind(Json(new_entrypoint.preferences)) - .bind(Json(preferences_user_data)) - .bind(new_entrypoint.description) - .bind(Json(new_entrypoint.actions)) - .bind(Json(actions_user_data)) - .bind(new_entrypoint.icon_path) - .bind(uuid) - .execute(&mut *tx) - .await?; + let query = r#" + INSERT OR REPLACE INTO plugin_entrypoint (id, plugin_id, name, enabled, type, preferences, preferences_user_data, description, actions, actions_user_data, icon_path, uuid) + VALUES( + :id, + :plugin_id, + :name, + :enabled, + :type, + :preferences, + :preferences_user_data, + :description, + :actions, + :actions_user_data, + :icon_path, + :uuid + ) + "#; + + tx.execute( + query, + named_params! { + ":id": new_entrypoint.id, + ":plugin_id": &new_plugin.id, + ":name": new_entrypoint.name, + ":enabled": enabled, + ":type": new_entrypoint.entrypoint_type, + ":preferences": serde_json::to_value(new_entrypoint.preferences)?, + ":preferences_user_data": serde_json::to_value(preferences_user_data)?, + ":description": new_entrypoint.description, + ":actions": serde_json::to_value(new_entrypoint.actions)?, + ":actions_user_data": serde_json::to_value(actions_user_data)?, + ":icon_path": new_entrypoint.icon_path, + ":uuid": uuid, + }, + )?; } for old_entrypoint_id in old_entrypoint_ids { // language=SQLite - sqlx::query("DELETE FROM plugin_entrypoint WHERE id = ?1") - .bind(&old_entrypoint_id) - .execute(&mut *tx) - .await?; + let query = "DELETE FROM plugin_entrypoint WHERE id = :id"; + + tx.execute( + query, + named_params! { + ":id": old_entrypoint_id + }, + )?; } - let mut old_asset_data_paths = self.get_all_asset_data_paths(&new_plugin.id, &mut *tx).await?; + let mut old_asset_data_paths = self.get_all_asset_data_paths(&new_plugin.id, &mut tx)?; for data in new_plugin.asset_data { old_asset_data_paths.remove(&data.path); // language=SQLite - sqlx::query("INSERT OR REPLACE INTO plugin_asset_data (plugin_id, path, data) VALUES(?1, ?2, ?3)") - .bind(&new_plugin.id) - .bind(&data.path) - .bind(&data.data) - .execute(&mut *tx) - .await?; + let query = r#" + INSERT OR REPLACE INTO plugin_asset_data (plugin_id, path, data) + VALUES(:plugin_id, :path, :data) + "#; + + tx.execute( + query, + named_params! { + ":plugin_id": new_plugin.id, + ":path": data.path, + ":data": data.data, + }, + )?; } for old_asset_data_path in old_asset_data_paths { // language=SQLite - sqlx::query("DELETE FROM plugin_asset_data WHERE plugin_id = ?1 AND path = ?2") - .bind(&new_plugin.id) - .bind(&old_asset_data_path) - .execute(&mut *tx) - .await?; + let query = "DELETE FROM plugin_asset_data WHERE plugin_id = :plugin_id AND path = :path"; + + tx.execute( + query, + named_params! { + ":plugin_id": new_plugin.id, + ":path": old_asset_data_path + }, + )?; } - tx.commit().await?; + tx.commit()?; Ok(()) } @@ -1301,3 +1447,9 @@ pub fn db_plugin_type_from_str(value: &str) -> DbPluginType { _ => panic!("illegal plugin_type: {}", value), } } + +trait RusqliteFromRow { + fn from_row(row: &Row<'_>) -> rusqlite::Result + where + Self: Sized; +} diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 771961e..a840afe 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -313,7 +313,7 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run let mut sender = sender.lock().await; if let Err(err) = send_message(JsMessageSide::Backend, &mut sender, JsMessage::Stop).await { - tracing::error!("Error when sending stop request to plugin runtime {:?}", err); + tracing::error!("Error when sending stop request to plugin runtime: {:?}", err); } } }); @@ -331,7 +331,7 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run } }) } => { - tracing::error!("Event loop has been stopped {:?}", plugin_id) + tracing::error!("Event loop has been stopped: {:?}", plugin_id) } _ = { tokio::task::unconstrained(async { @@ -345,7 +345,7 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run } } Err(err) => { - tracing::error!("Request loop faced an error {:?}", err); + tracing::error!("Server side of plugin request loop faced an error: {:?}", err); break; } } @@ -620,28 +620,22 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { let DbReadPlugin { name, .. } = self .repository .get_plugin_by_id(&self.plugin_id.to_string()) - .await .context("error when getting plugin by id")?; let entrypoints = self .repository .get_entrypoints_by_plugin_id(&self.plugin_id.to_string()) - .await .context("error when getting entrypoints by plugin id")?; let frecency_map = self .repository .get_frecency_for_plugin(&self.plugin_id.to_string()) - .await .context("error when getting frecency for plugin")?; let mut shortcuts = HashMap::new(); for DbReadPluginEntrypoint { id, .. } in &entrypoints { - let entrypoint_shortcuts = self - .repository - .action_shortcuts(&self.plugin_id.to_string(), id) - .await?; + let entrypoint_shortcuts = self.repository.action_shortcuts(&self.plugin_id.to_string(), id)?; shortcuts.insert(id.clone(), entrypoint_shortcuts); } @@ -730,8 +724,7 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { if let Some(path_to_asset) = &entrypoint.icon_path { let result = self .repository - .get_asset_data(&self.plugin_id.to_string(), path_to_asset) - .await; + .get_asset_data(&self.plugin_id.to_string(), path_to_asset); if let Ok(data) = result { icon_asset_data.insert((entrypoint.id.clone(), path_to_asset.clone()), data); @@ -809,10 +802,7 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { } async fn get_asset_data(&self, path: String) -> RequestResult> { - let data = self - .repository - .get_asset_data(&self.plugin_id.to_string(), &path) - .await?; + let data = self.repository.get_asset_data(&self.plugin_id.to_string(), &path)?; Ok(data) } @@ -820,8 +810,7 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { async fn get_entrypoint_generator_entrypoint_ids(&self) -> RequestResult> { let result = self .repository - .get_entrypoints_by_plugin_id(&self.plugin_id.to_string()) - .await? + .get_entrypoints_by_plugin_id(&self.plugin_id.to_string())? .into_iter() .filter(|entrypoint| entrypoint.enabled) .filter(|entrypoint| { @@ -841,7 +830,7 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { preferences, preferences_user_data, .. - } = self.repository.get_plugin_by_id(&self.plugin_id.to_string()).await?; + } = self.repository.get_plugin_by_id(&self.plugin_id.to_string())?; Ok(preferences_to_js(preferences, preferences_user_data)) } @@ -856,8 +845,7 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { .. } = self .repository - .get_entrypoint_by_id(&self.plugin_id.to_string(), &entrypoint_id.to_string()) - .await?; + .get_entrypoint_by_id(&self.plugin_id.to_string(), &entrypoint_id.to_string())?; Ok(preferences_to_js(preferences, preferences_user_data)) } @@ -867,7 +855,7 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { preferences, preferences_user_data, .. - } = self.repository.get_plugin_by_id(&self.plugin_id.to_string()).await?; + } = self.repository.get_plugin_by_id(&self.plugin_id.to_string())?; Ok(any_preferences_missing_value(preferences, preferences_user_data)) } @@ -879,8 +867,7 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { .. } = self .repository - .get_entrypoint_by_id(&self.plugin_id.to_string(), &entrypoint_id.to_string()) - .await?; + .get_entrypoint_by_id(&self.plugin_id.to_string(), &entrypoint_id.to_string())?; Ok(any_preferences_missing_value(preferences, preferences_user_data)) } @@ -974,18 +961,15 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { modifier_alt: bool, modifier_meta: bool, ) -> RequestResult> { - let result = self - .repository - .get_action_id_for_shortcut( - &self.plugin_id.to_string(), - &entrypoint_id.to_string(), - PhysicalKey::from_value(key), - modifier_shift, - modifier_control, - modifier_alt, - modifier_meta, - ) - .await?; + let result = self.repository.get_action_id_for_shortcut( + &self.plugin_id.to_string(), + &entrypoint_id.to_string(), + PhysicalKey::from_value(key), + modifier_shift, + modifier_control, + modifier_alt, + modifier_meta, + )?; Ok(result) } diff --git a/rust/server/src/plugins/loader.rs b/rust/server/src/plugins/loader.rs index 00be5f3..23547a5 100644 --- a/rust/server/src/plugins/loader.rs +++ b/rust/server/src/plugins/loader.rs @@ -74,20 +74,18 @@ impl PluginLoader { let plugin_data = PluginLoader::read_plugin_dir(temp_dir.path(), plugin_id_clone.clone()).await?; - data_db_repository - .save_plugin(DbWritePlugin { - id: plugin_data.id, - name: plugin_data.name, - description: plugin_data.description, - enabled: false, - code: plugin_data.code, - entrypoints: plugin_data.entrypoints, - asset_data: plugin_data.asset_data, - permissions: plugin_data.permissions, - plugin_type: db_plugin_type_to_str(DbPluginType::Normal).to_owned(), - preferences: plugin_data.preferences, - }) - .await?; + data_db_repository.save_plugin(DbWritePlugin { + id: plugin_data.id, + name: plugin_data.name, + description: plugin_data.description, + enabled: false, + code: plugin_data.code, + entrypoints: plugin_data.entrypoints, + asset_data: plugin_data.asset_data, + permissions: plugin_data.permissions, + plugin_type: db_plugin_type_to_str(DbPluginType::Normal).to_owned(), + preferences: plugin_data.preferences, + })?; anyhow::Ok(()) }); @@ -119,20 +117,18 @@ impl PluginLoader { .await .context(format!("Unable to read plugin: {}", &plugin_id.to_string()))?; - self.db_repository - .save_plugin(DbWritePlugin { - id: plugin_data.id, - name: plugin_data.name, - description: plugin_data.description, - enabled: true, - code: plugin_data.code, - entrypoints: plugin_data.entrypoints, - asset_data: plugin_data.asset_data, - permissions: plugin_data.permissions, - plugin_type: db_plugin_type_to_str(DbPluginType::Normal).to_owned(), - preferences: plugin_data.preferences, - }) - .await?; + self.db_repository.save_plugin(DbWritePlugin { + id: plugin_data.id, + name: plugin_data.name, + description: plugin_data.description, + enabled: true, + code: plugin_data.code, + entrypoints: plugin_data.entrypoints, + asset_data: plugin_data.asset_data, + permissions: plugin_data.permissions, + plugin_type: db_plugin_type_to_str(DbPluginType::Normal).to_owned(), + preferences: plugin_data.preferences, + })?; Ok(plugin_id) } @@ -147,20 +143,18 @@ impl PluginLoader { .await .context(format!("Unable to read plugin: {}", &plugin_id.to_string()))?; - self.db_repository - .save_plugin(DbWritePlugin { - id: plugin_data.id, - name: plugin_data.name, - description: plugin_data.description, - enabled: true, - code: plugin_data.code, - entrypoints: plugin_data.entrypoints, - asset_data: plugin_data.asset_data, - permissions: plugin_data.permissions, - plugin_type: db_plugin_type_to_str(DbPluginType::Bundled).to_owned(), - preferences: plugin_data.preferences, - }) - .await?; + self.db_repository.save_plugin(DbWritePlugin { + id: plugin_data.id, + name: plugin_data.name, + description: plugin_data.description, + enabled: true, + code: plugin_data.code, + entrypoints: plugin_data.entrypoints, + asset_data: plugin_data.asset_data, + permissions: plugin_data.permissions, + plugin_type: db_plugin_type_to_str(DbPluginType::Bundled).to_owned(), + preferences: plugin_data.preferences, + })?; Ok(plugin_id) } diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index df97083..9ef5b5d 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -79,7 +79,6 @@ pub mod js; mod loader; pub mod plugin_manifest; mod run_status; -mod runtime; pub mod settings; pub mod theme; @@ -339,7 +338,7 @@ impl ApplicationManager { let plugin_id = self.plugin_downloader.save_local_plugin(path).await?; - let plugin = self.db_repository.get_plugin_by_id(&plugin_id.to_string()).await?; + let plugin = self.db_repository.get_plugin_by_id(&plugin_id.to_string())?; self.reload_plugin(plugin_id.clone()).await?; @@ -402,8 +401,7 @@ impl ApplicationManager { let result = self .db_repository - .list_plugins_and_entrypoints() - .await? + .list_plugins_and_entrypoints()? .into_iter() .map(|(plugin, entrypoints)| { let plugin_id = PluginId::from_string(plugin.id); @@ -502,9 +500,7 @@ impl ApplicationManager { match (currently_running, currently_enabled, set_enabled) { (false, false, true) => { - self.db_repository - .set_plugin_enabled(&plugin_id.to_string(), true) - .await?; + self.db_repository.set_plugin_enabled(&plugin_id.to_string(), true)?; self.start_plugin(plugin_id).await?; } @@ -512,9 +508,7 @@ impl ApplicationManager { self.start_plugin(plugin_id).await?; } (true, true, false) => { - self.db_repository - .set_plugin_enabled(&plugin_id.to_string(), false) - .await?; + self.db_repository.set_plugin_enabled(&plugin_id.to_string(), false)?; self.stop_plugin(plugin_id.clone()).await; self.search_index.remove_for_plugin(plugin_id)?; @@ -545,9 +539,11 @@ impl ApplicationManager { enabled ); - self.db_repository - .set_plugin_entrypoint_enabled(&plugin_id.to_string(), &entrypoint_id.to_string(), enabled) - .await?; + self.db_repository.set_plugin_entrypoint_enabled( + &plugin_id.to_string(), + &entrypoint_id.to_string(), + enabled, + )?; self.reload_plugin(plugin_id.clone()).await?; @@ -633,14 +629,12 @@ impl ApplicationManager { let user_data = plugin_preference_user_data_to_db(preference_value); - self.db_repository - .set_preference_value( - plugin_id.to_string(), - entrypoint_id.map(|id| id.to_string()), - preference_id, - user_data, - ) - .await?; + self.db_repository.set_preference_value( + plugin_id.to_string(), + entrypoint_id.map(|id| id.to_string()), + preference_id, + user_data, + )?; self.reload_plugin(plugin_id.clone()).await?; @@ -658,7 +652,7 @@ impl ApplicationManager { self.reload_config().await?; - for plugin in self.db_repository.list_plugins().await? { + for plugin in self.db_repository.list_plugins()? { let plugin_id = PluginId::from_string(plugin.id); let running = self.run_status_holder.is_plugin_running(&plugin_id); match (running, plugin.enabled) { @@ -683,7 +677,7 @@ impl ApplicationManager { if running { self.stop_plugin(plugin_id.clone()).await; } - self.db_repository.remove_plugin(&plugin_id.to_string()).await?; + self.db_repository.remove_plugin(&plugin_id.to_string())?; self.search_index.remove_for_plugin(plugin_id)?; Ok(()) } @@ -851,7 +845,7 @@ impl ApplicationManager { } async fn is_plugin_enabled(&self, plugin_id: &PluginId) -> anyhow::Result { - self.db_repository.is_plugin_enabled(&plugin_id.to_string()).await + self.db_repository.is_plugin_enabled(&plugin_id.to_string()) } async fn action_shortcuts( @@ -861,7 +855,6 @@ impl ApplicationManager { ) -> anyhow::Result> { self.db_repository .action_shortcuts(&plugin_id.to_string(), &entrypoint_id.to_string()) - .await } async fn start_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { @@ -869,20 +862,18 @@ impl ApplicationManager { let plugin_id_str = plugin_id.to_string(); - let plugin = self.db_repository.get_plugin_by_id(&plugin_id_str).await?; + let plugin = self.db_repository.get_plugin_by_id(&plugin_id_str)?; let entrypoint_names = self .db_repository - .get_entrypoints_by_plugin_id(&plugin_id_str) - .await? + .get_entrypoints_by_plugin_id(&plugin_id_str)? .into_iter() .map(|entrypoint| (EntrypointId::from_string(entrypoint.id), entrypoint.name)) .collect::>(); let inline_view_entrypoint_id = self .db_repository - .get_inline_view_entrypoint_id_for_plugin(&plugin_id_str) - .await?; + .get_inline_view_entrypoint_id_for_plugin(&plugin_id_str)?; let receiver = self.command_broadcaster.subscribe(); @@ -970,8 +961,7 @@ impl ApplicationManager { async fn mark_entrypoint_frecency(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) { let result = self .db_repository - .mark_entrypoint_frecency(&plugin_id.to_string(), &entrypoint_id.to_string()) - .await; + .mark_entrypoint_frecency(&plugin_id.to_string(), &entrypoint_id.to_string()); if let Err(err) = &result { tracing::warn!( @@ -987,8 +977,7 @@ impl ApplicationManager { pub async fn inline_view_shortcuts(&self) -> anyhow::Result>> { let result: HashMap<_, _> = self .db_repository - .inline_view_shortcuts() - .await? + .inline_view_shortcuts()? .into_iter() .map(|(plugin_id, shortcuts)| (PluginId::from_string(plugin_id), shortcuts)) .collect(); diff --git a/rust/server/src/plugins/runtime.rs b/rust/server/src/plugins/runtime.rs deleted file mode 100644 index 8b13789..0000000 --- a/rust/server/src/plugins/runtime.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/rust/server/src/plugins/settings.rs b/rust/server/src/plugins/settings.rs index fd483bd..c580543 100644 --- a/rust/server/src/plugins/settings.rs +++ b/rust/server/src/plugins/settings.rs @@ -43,7 +43,7 @@ impl Settings { } pub async fn global_shortcut(&self) -> anyhow::Result)>> { - let settings = self.repository.get_settings().await?; + let settings = self.repository.get_settings()?; let data = settings.global_shortcut.map(|data| { let shortcut = PhysicalShortcut { @@ -65,7 +65,7 @@ impl Settings { let db_err = err.as_ref().map_err(|err| format!("{:#}", err)).err(); - let mut settings = self.repository.get_settings().await?; + let mut settings = self.repository.get_settings()?; settings.global_shortcut = shortcut.map(|shortcut| { DbSettingsGlobalShortcutData { @@ -80,19 +80,19 @@ impl Settings { } }); - self.repository.set_settings(settings).await?; + self.repository.set_settings(settings)?; err.map_err(Into::into) } pub async fn set_global_shortcut_error(&self, error: Option) -> anyhow::Result<()> { - let mut settings = self.repository.get_settings().await?; + let mut settings = self.repository.get_settings()?; if let Some(data) = &mut settings.global_shortcut { data.error = error } - self.repository.set_settings(settings).await?; + self.repository.set_settings(settings)?; Ok(()) } @@ -100,7 +100,7 @@ impl Settings { pub async fn global_entrypoint_shortcuts( &self, ) -> anyhow::Result)>> { - let settings = self.repository.get_settings().await?; + let settings = self.repository.get_settings()?; let data: HashMap<_, _> = settings .global_entrypoint_shortcuts .unwrap_or_default() @@ -143,7 +143,7 @@ impl Settings { let db_err = err.as_ref().map_err(|err| format!("{:#}", err)).err(); - let mut settings = self.repository.get_settings().await?; + let mut settings = self.repository.get_settings()?; let mut shortcuts: HashMap<_, _> = settings .global_entrypoint_shortcuts @@ -194,7 +194,7 @@ impl Settings { settings.global_entrypoint_shortcuts = Some(global_entrypoint_shortcuts); - self.repository.set_settings(settings).await?; + self.repository.set_settings(settings)?; Ok(()) } @@ -205,7 +205,7 @@ impl Settings { entrypoint_id: EntrypointId, error: Option, ) -> anyhow::Result<()> { - let mut settings = self.repository.get_settings().await?; + let mut settings = self.repository.get_settings()?; let mut shortcuts: HashMap<_, _> = settings .global_entrypoint_shortcuts @@ -239,13 +239,13 @@ impl Settings { settings.global_entrypoint_shortcuts = Some(global_entrypoint_shortcuts); - self.repository.set_settings(settings).await?; + self.repository.set_settings(settings)?; Ok(()) } pub async fn entrypoint_search_aliases(&self) -> anyhow::Result> { - let settings = self.repository.get_settings().await?; + let settings = self.repository.get_settings()?; let data: HashMap<_, _> = settings .entrypoint_search_aliases @@ -271,7 +271,7 @@ impl Settings { entrypoint_id: EntrypointId, alias: Option, ) -> anyhow::Result<()> { - let mut settings = self.repository.get_settings().await?; + let mut settings = self.repository.get_settings()?; let mut alias_data: HashMap<_, _> = settings .entrypoint_search_aliases @@ -306,7 +306,7 @@ impl Settings { settings.entrypoint_search_aliases = Some(alias_data); - self.repository.set_settings(settings).await?; + self.repository.set_settings(settings)?; Ok(()) } @@ -318,7 +318,7 @@ impl Settings { // TODO config - let settings = self.repository.get_settings().await?; + let settings = self.repository.get_settings()?; let theme = match &settings.theme { None => self.autodetect_theme(), @@ -341,7 +341,7 @@ impl Settings { // TODO config - let settings = self.repository.get_settings().await?; + let settings = self.repository.get_settings()?; match settings.theme { None => Ok(SettingsTheme::AutoDetect), @@ -352,7 +352,7 @@ impl Settings { } pub async fn set_theme_setting(&self, theme: SettingsTheme) -> anyhow::Result<()> { - let mut settings = self.repository.get_settings().await?; + let mut settings = self.repository.get_settings()?; settings.theme = match theme { SettingsTheme::AutoDetect => None, @@ -375,7 +375,7 @@ impl Settings { } }; - self.repository.set_settings(settings).await?; + self.repository.set_settings(settings)?; self.frontend_api.set_theme(theme).await?; @@ -383,7 +383,7 @@ impl Settings { } pub async fn window_position_mode_setting(&self) -> anyhow::Result { - let settings = self.repository.get_settings().await?; + let settings = self.repository.get_settings()?; let window_position_mode = match &settings.window_position_mode { None => WindowPositionMode::Static, @@ -394,7 +394,7 @@ impl Settings { } pub async fn set_window_position_mode_setting(&self, mode: WindowPositionMode) -> anyhow::Result<()> { - let mut settings = self.repository.get_settings().await?; + let mut settings = self.repository.get_settings()?; let window_position_mode = match mode { WindowPositionMode::Static => None, @@ -403,7 +403,7 @@ impl Settings { settings.window_position_mode = window_position_mode; - self.repository.set_settings(settings).await?; + self.repository.set_settings(settings)?; self.frontend_api.set_window_position_mode(mode).await?; diff --git a/rust/utils_macros/src/boundary_gen.rs b/rust/utils_macros/src/boundary_gen.rs new file mode 100644 index 0000000..7de3e6c --- /dev/null +++ b/rust/utils_macros/src/boundary_gen.rs @@ -0,0 +1,453 @@ +use convert_case::Case; +use convert_case::Casing; +use proc_macro::TokenStream; +use quote::quote; +use syn::FnArg; +use syn::ItemTrait; +use syn::Meta; +use syn::Pat; +use syn::PathArguments; +use syn::ReturnType; +use syn::TraitItem; +use syn::TraitItemFn; +use syn::Type; +use syn::parse_macro_input; +use syn::punctuated::Punctuated; + +pub fn boundary_gen(args: TokenStream, input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as ItemTrait); + let args = parse_macro_input!(args with Punctuated::::parse_terminated); + + let bincode_enabled = args.iter().any(|arg| { + match arg { + Meta::Path(path) => path.is_ident("bincode"), + _ => false, + } + }); + + let in_process_enabled = args.iter().any(|arg| { + match arg { + Meta::Path(path) => path.is_ident("in_process"), + _ => false, + } + }); + + let grpc_enabled = args.iter().any(|arg| { + match arg { + Meta::Path(path) => path.is_ident("grpc"), + _ => false, + } + }); + + let ItemTrait { ident, items, .. } = &input; + + let request_enum_name = syn::Ident::new( + &format!("{}RequestData", ident.to_string()), + proc_macro2::Span::call_site(), + ); + let response_enum_name = syn::Ident::new( + &format!("{}ResponseData", ident.to_string()), + proc_macro2::Span::call_site(), + ); + + let proxy_struct_name = syn::Ident::new(&format!("{}Proxy", ident.to_string()), proc_macro2::Span::call_site()); + + let request_enum_items: Vec<_> = items + .into_iter() + .filter_map(|item| { + match item { + TraitItem::Fn(item) => { + let enum_item_name = syn::Ident::new( + &item.sig.ident.to_string().to_case(Case::Pascal), + proc_macro2::Span::call_site(), + ); + + let inputs = get_inputs_as_ident_type_list(item); + + let result = quote!( + #enum_item_name { + #(#inputs)* + }, + ); + + Some(result) + } + _ => None, + } + }) + .collect(); + + let response_enum_items: Vec<_> = items + .into_iter() + .filter_map(|item| { + match item { + TraitItem::Fn(item) => { + let enum_item_name = syn::Ident::new( + &item.sig.ident.to_string().to_case(Case::Pascal), + proc_macro2::Span::call_site(), + ); + + let ty = get_output_as_type(&item); + + Some(quote! { + #enum_item_name { + data: #ty + }, + }) + } + _ => None, + } + }) + .collect(); + + let impl_fns: Vec<_> = items + .into_iter() + .filter_map(|item| { + match item { + TraitItem::Fn(item) => { + let sig = item.sig.clone(); + let ident = item.sig.ident.clone(); + + let enum_item_name = syn::Ident::new( + &item.sig.ident.to_string().to_case(Case::Pascal), + proc_macro2::Span::call_site(), + ); + + let field_names = get_inputs_as_idents_list(item); + + let result = quote!( + #sig { + let request = #request_enum_name::#enum_item_name { + #(#field_names)* + }; + + match self.request(request).await? { + #response_enum_name::#enum_item_name { data } => Ok(data), + value @ _ => panic!("Unexpected {} return type: {:?}", stringify!(#ident), value), + } + } + ); + + Some(result) + } + _ => None, + } + }) + .collect(); + + let handle_impl_fns: Vec<_> = items + .into_iter() + .filter_map(|item| { + match item { + TraitItem::Fn(item) => { + let ident = item.sig.ident.clone(); + + let enum_item_name = syn::Ident::new( + &item.sig.ident.to_string().to_case(Case::Pascal), + proc_macro2::Span::call_site(), + ); + + let field_names = get_inputs_as_idents_list(item); + + let result = quote!( + #request_enum_name::#enum_item_name { + #(#field_names)* + } => { + let data = api.#ident( + #(#field_names)* + ).await?; + + Ok(#response_enum_name::#enum_item_name { data }) + } + ); + + Some(result) + } + _ => None, + } + }) + .collect(); + + let bincode_derive = if bincode_enabled { + Some(quote!( + #[derive(bincode::Encode, bincode::Decode)] + )) + } else { + None + }; + + let proxy = if in_process_enabled { + Some(quote! { + #[derive(Clone)] + pub struct #proxy_struct_name { + request_sender: gauntlet_utils::channel::RequestSender<#request_enum_name, #response_enum_name>, + } + + impl #proxy_struct_name { + pub fn new(request_sender: gauntlet_utils::channel::RequestSender<#request_enum_name, #response_enum_name>) -> Self { + Self { request_sender } + } + + async fn request(&self, request: #request_enum_name) -> gauntlet_utils::channel::RequestResult<#response_enum_name> { + self.request_sender.send_receive(request).await + } + } + + impl #ident for #proxy_struct_name { + #(#impl_fns)* + } + + pub async fn handle_proxy_message(message: #request_enum_name, api: &impl #ident) -> anyhow::Result<#response_enum_name> { + match message { + #(#handle_impl_fns)* + } + } + }) + } else { + None + }; + + let grpc_wrap_functions: Vec<_> = items + .into_iter() + .filter_map(|item| { + match item { + TraitItem::Fn(item) => { + let fn_name = syn::Ident::new( + &ident.to_string().to_case(Case::Snake), + proc_macro2::Span::call_site(), + ); + + let ident = item.sig.ident.clone(); + let enum_item_name = syn::Ident::new( + &item.sig.ident.to_string().to_case(Case::Pascal), + proc_macro2::Span::call_site(), + ); + + + let field_names = get_inputs_as_idents_list(item); + let inputs = get_inputs_as_ident_type_list(item); + let ty = get_output_as_type(&item); + + Some(quote! { + async fn #ident( + &self, + #(#inputs)* + ) -> RequestResult<#ty> { + let request = #request_enum_name::#enum_item_name { + #(#field_names)* + }; + + let encoded: Vec = bincode::encode_to_vec(&request, bincode::config::standard()) + .map_err::(|err| anyhow::anyhow!("Unable to deserialize message with id: {:#}", err).into())?; + + let response = self.client.#fn_name(encoded).await + .map_err::(|err| anyhow::anyhow!("{:#}", err).into())?; + + let (decoded, _): (#response_enum_name, _) = bincode::decode_from_slice(&response[..], bincode::config::standard()) + .map_err::(|err| anyhow::anyhow!("Unable to deserialize message with id: {:#}", err).into())?; + + let #response_enum_name::#enum_item_name { data } = decoded else { + return Err(anyhow::anyhow!("Invalid return type from server: {:?}", decoded).into()); + }; + + Ok(data) + } + }) + } + _ => None, + } + }) + .collect(); + + let handle_grpc_match_arms: Vec<_> = items + .into_iter() + .filter_map(|item| { + match item { + TraitItem::Fn(item) => { + let ident = item.sig.ident.clone(); + let enum_item_name = syn::Ident::new( + &item.sig.ident.to_string().to_case(Case::Pascal), + proc_macro2::Span::call_site(), + ); + + let field_names = get_inputs_as_idents_list(item); + + let result = quote! { + #request_enum_name::#enum_item_name { + #(#field_names)* + } => { + let result = handler.#ident( + #(#field_names)* + ).await; + + match result { + Ok(result) => #response_enum_name::#enum_item_name { data: result }, + Err(err) => { + match err { + gauntlet_utils::channel::RequestError::Timeout => panic!("unsupported return error: {}", err), + gauntlet_utils::channel::RequestError::OtherSideWasDropped => panic!("unsupported return error: {}", err), + gauntlet_utils::channel::RequestError::Other { display } => Err(tonic::Status::internal(display))?, + } + } + } + } + }; + + Some(result) + } + _ => None, + } + }) + .collect(); + + let grpc = if grpc_enabled { + let fn_name = syn::Ident::new( + &format!("handle_grpc_request_{}", ident.to_string().to_case(Case::Snake)), + proc_macro2::Span::call_site(), + ); + + let result = quote! { + #[derive(Debug, Clone)] + pub struct #proxy_struct_name { + client: GrpcBackendApi + } + + impl #proxy_struct_name { + pub fn new( + client: GrpcBackendApi + ) -> Self { + Self { + client + } + } + } + + #[tonic::async_trait] + impl #ident for #proxy_struct_name { + #(#grpc_wrap_functions)* + } + + pub async fn #fn_name(handler: &(dyn #ident + Send + Sync), data: Vec) -> Result, tonic::Status> { + let (decoded, _): (#request_enum_name, _) = bincode::decode_from_slice(&data[..], bincode::config::standard()) + .map_err(|err| tonic::Status::unknown(format!("Unable to deserialize message with id: {:#}", err)))?; + + let response = match decoded { + #(#handle_grpc_match_arms)* + }; + + let encoded: Vec = bincode::encode_to_vec(&response, bincode::config::standard()) + .map_err(|err| tonic::Status::unknown(format!("Unable to deserialize message with id: {:#}", err)))?; + + Ok(encoded) + } + + }; + + Some(result) + } else { + None + }; + + quote!( + #input + + #[derive(Debug)] + #bincode_derive + pub enum #request_enum_name { + #(#request_enum_items)* + } + + #[derive(Debug)] + #bincode_derive + pub enum #response_enum_name { + #(#response_enum_items)* + } + + #proxy + + #grpc + ) + .into() +} + +fn get_inputs_as_idents_list(item: &TraitItemFn) -> Vec { + let field_names: Vec<_> = item + .sig + .inputs + .iter() + .filter_map(|item| { + match item { + FnArg::Receiver(_) => None, + FnArg::Typed(item) => { + match item.pat.as_ref() { + Pat::Ident(pat) => { + let ident = pat.ident.clone(); + let result = quote!(#ident,); + Some(result) + } + _ => None, + } + } + } + }) + .collect(); + + field_names +} + +fn get_inputs_as_ident_type_list(item: &TraitItemFn) -> Vec { + let inputs: Vec<_> = item + .sig + .inputs + .iter() + .filter_map(|item| { + match item { + FnArg::Receiver(_) => None, + FnArg::Typed(item) => { + match item.pat.as_ref() { + Pat::Ident(pat) => { + let ident = pat.ident.clone(); + let ty = item.ty.clone(); + let result = quote!(#ident: #ty,); + + Some(result) + } + _ => None, + } + } + } + }) + .collect(); + + inputs +} + +fn get_output_as_type(item: &TraitItemFn) -> Option { + let ty = match &item.sig.output { + ReturnType::Default => { + panic!("Plain () return type is not supported, return anyhow::Result<()>") + } + ReturnType::Type(_, ty) => { + let ty = match ty.as_ref() { + Type::Path(item) => { + let item = item.path.segments.last().unwrap(); + if &item.ident == "RequestResult" { + match &item.arguments { + PathArguments::AngleBracketed(item) => item.args.first(), + _ => return None, + } + } else { + return None; + } + } + _ => return None, + }; + + quote!( + #ty + ) + } + }; + + Some(ty) +} diff --git a/rust/utils_macros/src/lib.rs b/rust/utils_macros/src/lib.rs index 213e374..8c5bdbf 100644 --- a/rust/utils_macros/src/lib.rs +++ b/rust/utils_macros/src/lib.rs @@ -1,454 +1,14 @@ -use convert_case::Case; -use convert_case::Casing; use proc_macro::TokenStream; -use quote::quote; -use syn::FnArg; -use syn::ItemTrait; -use syn::Meta; -use syn::Pat; -use syn::PathArguments; -use syn::ReturnType; -use syn::TraitItem; -use syn::TraitItemFn; -use syn::Type; -use syn::parse_macro_input; -use syn::punctuated::Punctuated; + +mod boundary_gen; +mod rusqlite; #[proc_macro_attribute] pub fn boundary_gen(args: TokenStream, input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as ItemTrait); - let args = parse_macro_input!(args with Punctuated::::parse_terminated); - - let bincode_enabled = args.iter().any(|arg| { - match arg { - Meta::Path(path) => path.is_ident("bincode"), - _ => false, - } - }); - - let in_process_enabled = args.iter().any(|arg| { - match arg { - Meta::Path(path) => path.is_ident("in_process"), - _ => false, - } - }); - - let grpc_enabled = args.iter().any(|arg| { - match arg { - Meta::Path(path) => path.is_ident("grpc"), - _ => false, - } - }); - - let ItemTrait { ident, items, .. } = &input; - - let request_enum_name = syn::Ident::new( - &format!("{}RequestData", ident.to_string()), - proc_macro2::Span::call_site(), - ); - let response_enum_name = syn::Ident::new( - &format!("{}ResponseData", ident.to_string()), - proc_macro2::Span::call_site(), - ); - - let proxy_struct_name = syn::Ident::new(&format!("{}Proxy", ident.to_string()), proc_macro2::Span::call_site()); - - let request_enum_items: Vec<_> = items - .into_iter() - .filter_map(|item| { - match item { - TraitItem::Fn(item) => { - let enum_item_name = syn::Ident::new( - &item.sig.ident.to_string().to_case(Case::Pascal), - proc_macro2::Span::call_site(), - ); - - let inputs = get_inputs_as_ident_type_list(item); - - let result = quote!( - #enum_item_name { - #(#inputs)* - }, - ); - - Some(result) - } - _ => None, - } - }) - .collect(); - - let response_enum_items: Vec<_> = items - .into_iter() - .filter_map(|item| { - match item { - TraitItem::Fn(item) => { - let enum_item_name = syn::Ident::new( - &item.sig.ident.to_string().to_case(Case::Pascal), - proc_macro2::Span::call_site(), - ); - - let ty = get_output_as_type(&item); - - Some(quote! { - #enum_item_name { - data: #ty - }, - }) - } - _ => None, - } - }) - .collect(); - - let impl_fns: Vec<_> = items - .into_iter() - .filter_map(|item| { - match item { - TraitItem::Fn(item) => { - let sig = item.sig.clone(); - let ident = item.sig.ident.clone(); - - let enum_item_name = syn::Ident::new( - &item.sig.ident.to_string().to_case(Case::Pascal), - proc_macro2::Span::call_site(), - ); - - let field_names = get_inputs_as_idents_list(item); - - let result = quote!( - #sig { - let request = #request_enum_name::#enum_item_name { - #(#field_names)* - }; - - match self.request(request).await? { - #response_enum_name::#enum_item_name { data } => Ok(data), - value @ _ => panic!("Unexpected {} return type: {:?}", stringify!(#ident), value), - } - } - ); - - Some(result) - } - _ => None, - } - }) - .collect(); - - let handle_impl_fns: Vec<_> = items - .into_iter() - .filter_map(|item| { - match item { - TraitItem::Fn(item) => { - let ident = item.sig.ident.clone(); - - let enum_item_name = syn::Ident::new( - &item.sig.ident.to_string().to_case(Case::Pascal), - proc_macro2::Span::call_site(), - ); - - let field_names = get_inputs_as_idents_list(item); - - let result = quote!( - #request_enum_name::#enum_item_name { - #(#field_names)* - } => { - let data = api.#ident( - #(#field_names)* - ).await?; - - Ok(#response_enum_name::#enum_item_name { data }) - } - ); - - Some(result) - } - _ => None, - } - }) - .collect(); - - let bincode_derive = if bincode_enabled { - Some(quote!( - #[derive(bincode::Encode, bincode::Decode)] - )) - } else { - None - }; - - let proxy = if in_process_enabled { - Some(quote! { - #[derive(Clone)] - pub struct #proxy_struct_name { - request_sender: gauntlet_utils::channel::RequestSender<#request_enum_name, #response_enum_name>, - } - - impl #proxy_struct_name { - pub fn new(request_sender: gauntlet_utils::channel::RequestSender<#request_enum_name, #response_enum_name>) -> Self { - Self { request_sender } - } - - async fn request(&self, request: #request_enum_name) -> gauntlet_utils::channel::RequestResult<#response_enum_name> { - self.request_sender.send_receive(request).await - } - } - - impl #ident for #proxy_struct_name { - #(#impl_fns)* - } - - pub async fn handle_proxy_message(message: #request_enum_name, api: &impl #ident) -> anyhow::Result<#response_enum_name> { - match message { - #(#handle_impl_fns)* - } - } - }) - } else { - None - }; - - let grpc_wrap_functions: Vec<_> = items - .into_iter() - .filter_map(|item| { - match item { - TraitItem::Fn(item) => { - let fn_name = syn::Ident::new( - &ident.to_string().to_case(Case::Snake), - proc_macro2::Span::call_site(), - ); - - let ident = item.sig.ident.clone(); - let enum_item_name = syn::Ident::new( - &item.sig.ident.to_string().to_case(Case::Pascal), - proc_macro2::Span::call_site(), - ); - - - let field_names = get_inputs_as_idents_list(item); - let inputs = get_inputs_as_ident_type_list(item); - let ty = get_output_as_type(&item); - - Some(quote! { - async fn #ident( - &self, - #(#inputs)* - ) -> RequestResult<#ty> { - let request = #request_enum_name::#enum_item_name { - #(#field_names)* - }; - - let encoded: Vec = bincode::encode_to_vec(&request, bincode::config::standard()) - .map_err::(|err| anyhow::anyhow!("Unable to deserialize message with id: {:#}", err).into())?; - - let response = self.client.#fn_name(encoded).await - .map_err::(|err| anyhow::anyhow!("{:#}", err).into())?; - - let (decoded, _): (#response_enum_name, _) = bincode::decode_from_slice(&response[..], bincode::config::standard()) - .map_err::(|err| anyhow::anyhow!("Unable to deserialize message with id: {:#}", err).into())?; - - let #response_enum_name::#enum_item_name { data } = decoded else { - return Err(anyhow::anyhow!("Invalid return type from server: {:?}", decoded).into()); - }; - - Ok(data) - } - }) - } - _ => None, - } - }) - .collect(); - - let handle_grpc_match_arms: Vec<_> = items - .into_iter() - .filter_map(|item| { - match item { - TraitItem::Fn(item) => { - let ident = item.sig.ident.clone(); - let enum_item_name = syn::Ident::new( - &item.sig.ident.to_string().to_case(Case::Pascal), - proc_macro2::Span::call_site(), - ); - - let field_names = get_inputs_as_idents_list(item); - - let result = quote! { - #request_enum_name::#enum_item_name { - #(#field_names)* - } => { - let result = handler.#ident( - #(#field_names)* - ).await; - - match result { - Ok(result) => #response_enum_name::#enum_item_name { data: result }, - Err(err) => { - match err { - gauntlet_utils::channel::RequestError::Timeout => panic!("unsupported return error: {}", err), - gauntlet_utils::channel::RequestError::OtherSideWasDropped => panic!("unsupported return error: {}", err), - gauntlet_utils::channel::RequestError::Other { display } => Err(tonic::Status::internal(display))?, - } - } - } - } - }; - - Some(result) - } - _ => None, - } - }) - .collect(); - - let grpc = if grpc_enabled { - let fn_name = syn::Ident::new( - &format!("handle_grpc_request_{}", ident.to_string().to_case(Case::Snake)), - proc_macro2::Span::call_site(), - ); - - let result = quote! { - #[derive(Debug, Clone)] - pub struct #proxy_struct_name { - client: GrpcBackendApi - } - - impl #proxy_struct_name { - pub fn new( - client: GrpcBackendApi - ) -> Self { - Self { - client - } - } - } - - #[tonic::async_trait] - impl #ident for #proxy_struct_name { - #(#grpc_wrap_functions)* - } - - pub async fn #fn_name(handler: &(dyn #ident + Send + Sync), data: Vec) -> Result, tonic::Status> { - let (decoded, _): (#request_enum_name, _) = bincode::decode_from_slice(&data[..], bincode::config::standard()) - .map_err(|err| tonic::Status::unknown(format!("Unable to deserialize message with id: {:#}", err)))?; - - let response = match decoded { - #(#handle_grpc_match_arms)* - }; - - let encoded: Vec = bincode::encode_to_vec(&response, bincode::config::standard()) - .map_err(|err| tonic::Status::unknown(format!("Unable to deserialize message with id: {:#}", err)))?; - - Ok(encoded) - } - - }; - - Some(result) - } else { - None - }; - - quote!( - #input - - #[derive(Debug)] - #bincode_derive - pub enum #request_enum_name { - #(#request_enum_items)* - } - - #[derive(Debug)] - #bincode_derive - pub enum #response_enum_name { - #(#response_enum_items)* - } - - #proxy - - #grpc - ) - .into() + boundary_gen::boundary_gen(args, input) } -fn get_inputs_as_idents_list(item: &TraitItemFn) -> Vec { - let field_names: Vec<_> = item - .sig - .inputs - .iter() - .filter_map(|item| { - match item { - FnArg::Receiver(_) => None, - FnArg::Typed(item) => { - match item.pat.as_ref() { - Pat::Ident(pat) => { - let ident = pat.ident.clone(); - let result = quote!(#ident,); - Some(result) - } - _ => None, - } - } - } - }) - .collect(); - - field_names -} - -fn get_inputs_as_ident_type_list(item: &TraitItemFn) -> Vec { - let inputs: Vec<_> = item - .sig - .inputs - .iter() - .filter_map(|item| { - match item { - FnArg::Receiver(_) => None, - FnArg::Typed(item) => { - match item.pat.as_ref() { - Pat::Ident(pat) => { - let ident = pat.ident.clone(); - let ty = item.ty.clone(); - let result = quote!(#ident: #ty,); - - Some(result) - } - _ => None, - } - } - } - }) - .collect(); - - inputs -} - -fn get_output_as_type(item: &TraitItemFn) -> Option { - let ty = match &item.sig.output { - ReturnType::Default => { - panic!("Plain () return type is not supported, return anyhow::Result<()>") - } - ReturnType::Type(_, ty) => { - let ty = match ty.as_ref() { - Type::Path(item) => { - let item = item.path.segments.last().unwrap(); - if &item.ident == "RequestResult" { - match &item.arguments { - PathArguments::AngleBracketed(item) => item.args.first(), - _ => return None, - } - } else { - return None; - } - } - _ => return None, - }; - - quote!( - #ty - ) - } - }; - - Some(ty) +#[proc_macro_derive(RusqliteFromRow, attributes(rusqlite))] +pub fn derive_rusqlite(item: TokenStream) -> TokenStream { + rusqlite::derive_rusqlite(item) } diff --git a/rust/utils_macros/src/rusqlite.rs b/rust/utils_macros/src/rusqlite.rs new file mode 100644 index 0000000..6dfd492 --- /dev/null +++ b/rust/utils_macros/src/rusqlite.rs @@ -0,0 +1,110 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::Attribute; +use syn::Data; +use syn::DeriveInput; +use syn::Expr; +use syn::Meta; +use syn::MetaNameValue; +use syn::Path; +use syn::parse_macro_input; + +pub fn derive_rusqlite(item: TokenStream) -> TokenStream { + let item = parse_macro_input!(item as DeriveInput); + + let Data::Struct(data) = item.data else { + panic!() + }; + + let struct_name = item.ident; + + let output: Vec<_> = data + .fields + .iter() + .map(|field| { + let field_name = field.ident.clone(); + + let rename = field.attrs.iter().find_map(|attr| get_attr_rename(attr)); + let json = field.attrs.iter().any(|arg| is_attr_json(arg)); + + let column_name = match rename { + None => { + quote! { + stringify!(#field_name) + } + } + Some(rename) => { + quote! { + #rename + } + } + }; + + let value = if json { + quote! { + serde_json::from_value(row.get(#column_name)?).map_err(|err| rusqlite::Error::ToSqlConversionFailure(Box::new(err)))? + } + } else { + quote! { + row.get(#column_name)? + } + }; + + quote! { + #field_name: #value, + } + }) + .collect(); + + let result = quote! { + impl RusqliteFromRow for #struct_name { + fn from_row(row: &Row<'_>) -> rusqlite::Result { + Ok(Self { + #(#output)* + }) + } + } + }; + + result.into() +} + +fn is_attr_json(attr: &Attribute) -> bool { + match &attr.meta { + Meta::List(list) => { + let rusqlite = list.path.is_ident("rusqlite"); + if rusqlite { + let Ok(path) = list.parse_args::() else { + return false; + }; + + path.is_ident("json") + } else { + false + } + } + _ => false, + } +} + +fn get_attr_rename(attr: &Attribute) -> Option { + match &attr.meta { + Meta::List(list) => { + let rusqlite = list.path.is_ident("rusqlite"); + if rusqlite { + let Ok(name_value) = list.parse_args::() else { + return None; + }; + + if !name_value.path.is_ident("rename") { + return None; + } + + Some(name_value.value) + } else { + None + } + } + _ => None, + } +} From 5181a366e78b09540636c1a9e6bdb48ea087e897 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Wed, 28 May 2025 20:06:16 +0200 Subject: [PATCH 10/91] Update lots of dependencies in Cargo.lock --- CHANGELOG.md | 2 + Cargo.lock | 2527 +++++++++++++++++-------- rust/common_plugin_runtime/src/lib.rs | 2 +- rust/plugin_runtime/src/deno.rs | 1 - 4 files changed, 1691 insertions(+), 841 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d902ad8..f2e4682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://git ## [Unreleased] +- Updated Deno to 2.3.3 + ## [19] - 2025-05-11 ### General diff --git a/Cargo.lock b/Cargo.lock index f0fe1b0..70e2d54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "ab_glyph" version = "0.2.29" @@ -103,19 +113,19 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.2.15", + "getrandom 0.3.3", "once_cell", "version_check", "zerocopy", @@ -240,11 +250,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" dependencies = [ "anstyle", + "once_cell_polyfill", "windows-sys 0.59.0", ] @@ -274,19 +285,21 @@ checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" [[package]] name = "arboard" -version = "3.4.1" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" +checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70" dependencies = [ "clipboard-win", - "core-graphics 0.23.2", - "image 0.25.5", + "image 0.25.6", "log", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", + "objc2 0.6.1", + "objc2-app-kit 0.3.1", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.1", "parking_lot 0.12.3", - "windows-sys 0.48.0", + "percent-encoding", + "windows-sys 0.59.0", "wl-clipboard-rs", "x11rb", ] @@ -329,13 +342,19 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "ash" version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "libloading 0.8.6", + "libloading 0.8.8", ] [[package]] @@ -378,10 +397,22 @@ dependencies = [ ] [[package]] -name = "async-broadcast" -version = "0.7.1" +name = "ast_node" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +checksum = "91fb5864e2f5bf9fd9797b94b2dfd1554d4c3092b535008b27d7e15c86675a2f" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common 1.0.0", + "syn 2.0.101", +] + +[[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", @@ -403,11 +434,11 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.18" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" dependencies = [ - "brotli 7.0.0", + "brotli 8.0.1", "flate2", "futures-core", "memchr", @@ -417,14 +448,15 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", + "pin-project-lite", "slab", ] @@ -441,9 +473,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" dependencies = [ "async-lock", "cfg-if", @@ -452,7 +484,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix", + "rustix 1.0.7", "slab", "tracing", "windows-sys 0.59.0", @@ -477,9 +509,9 @@ checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" [[package]] name = "async-process" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" dependencies = [ "async-channel", "async-io", @@ -490,7 +522,7 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "rustix", + "rustix 1.0.7", "tracing", ] @@ -507,9 +539,9 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" dependencies = [ "async-io", "async-lock", @@ -517,7 +549,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix", + "rustix 1.0.7", "signal-hook-registry", "slab", "windows-sys 0.59.0", @@ -597,11 +629,11 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "184f5e6cce583a9db6b6f8d772a42cfce5b78e7c3ef26118cec3ce4c8c14969b" dependencies = [ - "http 1.2.0", + "http 1.3.1", "log", "rustls 0.22.4", "url", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -623,9 +655,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "av1-grain" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" dependencies = [ "anyhow", "arrayvec", @@ -637,9 +669,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62" +checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" dependencies = [ "arrayvec", ] @@ -654,7 +686,7 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "itoa", @@ -680,7 +712,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "mime", @@ -699,9 +731,9 @@ checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -736,40 +768,40 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64-simd" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" -dependencies = [ - "simd-abstraction", -] - [[package]] name = "base64-simd" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" dependencies = [ - "outref 0.5.1", + "outref", "vsimd", ] [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "basic-toml" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" dependencies = [ "serde", ] +[[package]] +name = "better_scoped_tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50fd297a11c709be8348aec039c8b91de16075d2b2bdaee1bd562c0875993664" +dependencies = [ + "scoped-tls", +] + [[package]] name = "bincode" version = "1.3.3" @@ -781,19 +813,20 @@ dependencies = [ [[package]] name = "bincode" -version = "2.0.0-rc.3" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11ea1a0346b94ef188834a65c068a03aec181c94896d481d7a0a40d85b0ce95" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" dependencies = [ "bincode_derive", "serde", + "unty", ] [[package]] name = "bincode_derive" -version = "2.0.0-rc.3" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e30759b3b99a1b802a7a3aa21c85c3ded5c28e1c83170d82d70f08bbf7f3e4c" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" dependencies = [ "virtue", ] @@ -813,7 +846,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "shlex", "syn 2.0.101", ] @@ -923,6 +956,15 @@ dependencies = [ "objc2 0.5.2", ] +[[package]] +name = "block2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" +dependencies = [ + "objc2 0.6.1", +] + [[package]] name = "blocking" version = "1.6.1" @@ -954,25 +996,35 @@ checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", - "brotli-decompressor", + "brotli-decompressor 4.0.3", ] [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", - "brotli-decompressor", + "brotli-decompressor 5.0.0", ] [[package]] name = "brotli-decompressor" -version = "4.0.1" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -980,9 +1032,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "serde", @@ -990,15 +1042,15 @@ dependencies = [ [[package]] name = "built" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b" +checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" dependencies = [ "allocator-api2", ] @@ -1020,9 +1072,9 @@ dependencies = [ [[package]] name = "bytemuck_derive" -version = "1.8.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" +checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" dependencies = [ "proc-macro2", "quote", @@ -1043,9 +1095,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cacao" @@ -1105,7 +1157,7 @@ dependencies = [ "bitflags 2.9.1", "log", "polling", - "rustix", + "rustix 0.38.44", "slab", "thiserror 1.0.69", ] @@ -1118,7 +1170,7 @@ checksum = "10929724661d1c43856fd87c7a127ae944ec55579134fb485e4136fb6a46fdcb" dependencies = [ "bitflags 2.9.1", "polling", - "rustix", + "rustix 0.38.44", "slab", "tracing", ] @@ -1130,7 +1182,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ "calloop 0.13.0", - "rustix", + "rustix 0.38.44", "wayland-backend", "wayland-client", ] @@ -1142,7 +1194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a7a1dbbe026a55ef47a500b123af5a9a0914520f061d467914cf21be95daf" dependencies = [ "calloop 0.14.2", - "rustix", + "rustix 0.38.44", "wayland-backend", "wayland-client", ] @@ -1189,18 +1241,27 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.19.1" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", - "semver 1.0.24", + "semver 1.0.26", "serde", "serde_json", "thiserror 2.0.12", ] +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + [[package]] name = "cbc" version = "0.1.2" @@ -1272,9 +1333,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1282,7 +1343,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1303,14 +1364,14 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading 0.8.6", + "libloading 0.8.8", ] [[package]] name = "clap" -version = "4.5.23" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" dependencies = [ "clap_builder", "clap_derive", @@ -1318,9 +1379,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" dependencies = [ "anstream", "anstyle", @@ -1330,9 +1391,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -1392,7 +1453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ "termcolor", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -1438,6 +1499,34 @@ dependencies = [ "memchr", ] +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "serde", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1468,7 +1557,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "once_cell", "tiny-keccak", ] @@ -1509,9 +1598,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -1556,7 +1645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ "bitflags 2.9.1", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-graphics-types 0.2.0", "foreign-types 0.5.0", "libc", @@ -1580,20 +1669,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ "bitflags 2.9.1", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "libc", ] [[package]] name = "cosmic-protocols" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-protocols.git#d218c76b58c7a3b20dd5e7943f93fc306a1b81b8" +source = "git+https://github.com/pop-os/cosmic-protocols.git#1425bd44ed2b318a552201cc752ae11f2f483ef5" dependencies = [ "bitflags 2.9.1", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.5", - "wayland-protocols-wlr 0.3.5", + "wayland-protocols", + "wayland-protocols-wlr", "wayland-scanner", "wayland-server", ] @@ -1623,9 +1712,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -1674,7 +1763,7 @@ dependencies = [ "hashbrown 0.14.5", "log", "regalloc2", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "serde", "smallvec", "target-lexicon 0.13.2", @@ -1764,9 +1853,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -1798,9 +1887,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-bigint" @@ -1842,9 +1931,9 @@ dependencies = [ [[package]] name = "cursor-icon" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "curve25519-dalek" @@ -1891,9 +1980,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -1901,9 +1990,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", @@ -1915,9 +2004,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", @@ -1939,9 +2028,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "data-url" @@ -1975,6 +2064,48 @@ dependencies = [ "byteorder", ] +[[package]] +name = "deno_ast" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59d2c5dcead329b1382472f0ca026839f33a86d897b47cf6d9cfa21c520b69c6" +dependencies = [ + "base64 0.22.1", + "capacity_builder", + "deno_error", + "deno_media_type", + "deno_terminal", + "dprint-swc-ext", + "percent-encoding", + "serde", + "sourcemap", + "swc_atoms", + "swc_common", + "swc_config", + "swc_config_macro", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_codegen_macros", + "swc_ecma_loader", + "swc_ecma_parser", + "swc_ecma_transforms_base", + "swc_ecma_transforms_classes", + "swc_ecma_transforms_macros", + "swc_ecma_transforms_proposal", + "swc_ecma_transforms_react", + "swc_ecma_transforms_typescript", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_eq_ignore_macros", + "swc_macros_common 1.0.0", + "swc_visit", + "swc_visit_macros", + "text_lines", + "thiserror 2.0.12", + "unicode-width 0.2.0", + "url", +] + [[package]] name = "deno_broadcast_channel" version = "0.199.0" @@ -2004,7 +2135,7 @@ dependencies = [ "deno_core", "deno_error", "futures", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", @@ -2035,7 +2166,7 @@ dependencies = [ "deno_error", "deno_media_type", "deno_path_util", - "http 1.2.0", + "http 1.3.1", "indexmap 2.9.0", "log", "once_cell", @@ -2057,7 +2188,7 @@ dependencies = [ "bytemuck", "deno_core", "deno_error", - "image 0.25.5", + "image 0.25.6", "lcms2", "num-traits", "thiserror 2.0.12", @@ -2252,16 +2383,16 @@ dependencies = [ "deno_tls", "dyn-clone", "error_reporter", - "h2 0.4.7", + "h2 0.4.10", "hickory-resolver", - "http 1.2.0", + "http 1.3.1", "http-body-util", "hyper 1.6.0", "hyper-rustls", "hyper-util", "ipnet", "percent-encoding", - "rustls-webpki", + "rustls-webpki 0.102.8", "serde", "serde_json", "thiserror 2.0.12", @@ -2345,7 +2476,7 @@ dependencies = [ "deno_websocket", "flate2", "http 0.2.12", - "http 1.2.0", + "http 1.3.1", "httparse", "hyper 0.14.32", "hyper 1.6.0", @@ -2414,7 +2545,7 @@ dependencies = [ "denokv_remote", "denokv_sqlite", "faster-hex 0.10.0", - "http 1.2.0", + "http 1.3.1", "http-body-util", "log", "num-bigint", @@ -2544,9 +2675,9 @@ dependencies = [ "elliptic-curve", "errno", "faster-hex 0.10.0", - "h2 0.4.7", + "h2 0.4.10", "hkdf", - "http 1.2.0", + "http 1.3.1", "http-body-util", "hyper 1.6.0", "hyper-util", @@ -2588,12 +2719,12 @@ dependencies = [ "tokio-eld", "tower-service", "url", - "webpki-root-certs", + "webpki-root-certs 0.26.11", "winapi", "windows-sys 0.59.0", "x25519-dalek", "x509-parser", - "yoke", + "yoke 0.7.5", ] [[package]] @@ -2782,6 +2913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bee2725c7610460a10b771ea5fecece28bf9e56f7c87b44d9fda4992490afeb5" dependencies = [ "color-print", + "deno_ast", "deno_broadcast_channel", "deno_cache", "deno_canvas", @@ -2816,7 +2948,7 @@ dependencies = [ "deno_webstorage", "encoding_rs", "fastwebsockets", - "http 1.2.0", + "http 1.3.1", "http-body-util", "hyper 1.6.0", "hyper-util", @@ -2903,21 +3035,21 @@ dependencies = [ "deno_core", "deno_error", "deno_native_certs", - "rustls 0.23.20", + "rustls 0.23.27", "rustls-pemfile", "rustls-tokio-stream", - "rustls-webpki", + "rustls-webpki 0.102.8", "serde", "thiserror 2.0.12", "tokio", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] name = "deno_unsync" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d774fd83f26b24f0805a6ab8b26834a0d06ceac0db517b769b1e4633c96a2057" +checksum = "47c618b51088b3ac67f15c69b3ed7620ba3a7d495e5a090186df9424b5ab623e" dependencies = [ "futures", "parking_lot 0.12.3", @@ -2942,7 +3074,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b0885564bfade3284b26a29cb32a67ba7b75fe329b7f12bc0e26815acf7ae1c" dependencies = [ "async-trait", - "base64-simd 0.8.0", + "base64-simd", "bytes", "deno_core", "deno_error", @@ -2997,8 +3129,8 @@ dependencies = [ "deno_permissions", "deno_tls", "fastwebsockets", - "h2 0.4.7", - "http 1.2.0", + "h2 0.4.10", + "http 1.3.1", "http-body-util", "hyper 1.6.0", "hyper-util", @@ -3060,7 +3192,7 @@ dependencies = [ "deno_error", "denokv_proto", "futures", - "http 1.2.0", + "http 1.3.1", "log", "prost", "rand 0.8.5", @@ -3113,9 +3245,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "der_derive", @@ -3150,25 +3282,14 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", ] -[[package]] -name = "derive-new" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "derive_builder" version = "0.20.2" @@ -3245,6 +3366,15 @@ dependencies = [ "dirs-sys 0.4.1", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys 0.5.0", +] + [[package]] name = "dirs-sys" version = "0.3.7" @@ -3252,7 +3382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -3264,10 +3394,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.6", "windows-sys 0.48.0", ] +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.59.0", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -3301,7 +3443,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.6", + "libloading 0.8.8", ] [[package]] @@ -3362,9 +3504,9 @@ checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" [[package]] name = "document-features" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" dependencies = [ "litrs", ] @@ -3383,9 +3525,24 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" + +[[package]] +name = "dprint-swc-ext" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a09827d6db1a3af25e105553d674ee9019be58fa3d6745c2a2803f8ce8e3eb8" +dependencies = [ + "num-bigint", + "rustc-hash 2.1.1", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "text_lines", +] [[package]] name = "drm" @@ -3397,7 +3554,7 @@ dependencies = [ "bytemuck", "drm-ffi", "drm-fourcc", - "rustix", + "rustix 0.38.44", ] [[package]] @@ -3407,7 +3564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53" dependencies = [ "drm-sys", - "rustix", + "rustix 0.38.44", ] [[package]] @@ -3450,9 +3607,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "ecb" @@ -3526,9 +3683,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" @@ -3653,9 +3810,9 @@ dependencies = [ [[package]] name = "enumflags2" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" dependencies = [ "enumflags2_derive", "serde", @@ -3663,9 +3820,9 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", @@ -3680,15 +3837,15 @@ checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" dependencies = [ "serde", "typeid", @@ -3696,9 +3853,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.10" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -3706,9 +3863,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.3.1" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" [[package]] name = "error_reporter" @@ -3718,9 +3875,9 @@ checksum = "31ae425815400e5ed474178a7a22e275a9687086a12ca63ec793ff292d8fdae8" [[package]] name = "etagere" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e2f1e3be19fb10f549be8c1bf013e8675b4066c445e36eb76d2ebb2f54ee495" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" dependencies = [ "euclid", "svg_fmt", @@ -3737,9 +3894,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -3748,9 +3905,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ "event-listener", "pin-project-lite", @@ -3842,13 +3999,13 @@ dependencies = [ [[package]] name = "fd-lock" -version = "4.0.2" +version = "4.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", - "rustix", - "windows-sys 0.52.0", + "rustix 1.0.7", + "windows-sys 0.59.0", ] [[package]] @@ -3862,9 +4019,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "rand_core 0.6.4", "subtle", @@ -3911,10 +4068,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] -name = "flate2" -version = "1.0.35" +name = "fixedbitset" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "flate2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide 0.8.8", @@ -3943,9 +4106,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "font-types" @@ -3958,9 +4121,9 @@ dependencies = [ [[package]] name = "fontconfig-parser" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" dependencies = [ "roxmltree", ] @@ -4073,6 +4236,17 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "from_variant" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d7ccf961415e7aa17ef93dcb6c2441faaa8e768abe09e659b908089546f74c5" +dependencies = [ + "proc-macro2", + "swc_macros_common 1.0.0", + "syn 2.0.101", +] + [[package]] name = "fs3" version = "0.5.0" @@ -4090,7 +4264,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7e180ac76c23b45e767bd7ae9579bc0bb458618c4bc71835926e098e61d15f8" dependencies = [ - "rustix", + "rustix 0.38.44", "windows-sys 0.52.0", ] @@ -4170,9 +4344,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ "fastrand", "futures-core", @@ -4259,7 +4433,7 @@ dependencies = [ "iced_aw", "iced_fonts", "iced_layershell", - "image 0.25.5", + "image 0.25.6", "itertools 0.13.0", "objc2-app-kit 0.2.2", "once_cell", @@ -4277,7 +4451,7 @@ version = "0.0.0" dependencies = [ "anyhow", "base64 0.22.1", - "bincode 2.0.0-rc.3", + "bincode 2.0.1", "bytes", "convert_case 0.6.0", "directories", @@ -4301,7 +4475,7 @@ name = "gauntlet-common-plugin-runtime" version = "0.1.0" dependencies = [ "anyhow", - "bincode 2.0.0-rc.3", + "bincode 2.0.1", "gauntlet-common", "gauntlet-utils", "gauntlet-utils-macros", @@ -4364,7 +4538,7 @@ name = "gauntlet-plugin-runtime" version = "0.1.0" dependencies = [ "anyhow", - "bincode 2.0.0-rc.3", + "bincode 2.0.1", "bytes", "cacao", "cosmic-protocols", @@ -4381,7 +4555,7 @@ dependencies = [ "gauntlet-component-model", "gauntlet-utils", "icns", - "image 0.25.5", + "image 0.25.6", "indexmap 2.9.0", "interprocess", "libc", @@ -4406,8 +4580,8 @@ dependencies = [ "uuid", "walkdir", "wayland-client", - "wayland-protocols-wlr 0.3.5", - "which 7.0.1", + "wayland-protocols-wlr", + "which 7.0.3", "windows 0.58.0", "x11rb", ] @@ -4441,7 +4615,7 @@ dependencies = [ "gauntlet-utils", "gauntlet-utils-macros", "git2", - "image 0.25.5", + "image 0.25.6", "include_dir", "interprocess", "itertools 0.13.0", @@ -4584,9 +4758,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", @@ -4710,12 +4884,13 @@ dependencies = [ [[package]] name = "gix-path" -version = "0.10.13" +version = "0.10.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc292ef1a51e340aeb0e720800338c805975724c1dfbd243185452efd8645b7" +checksum = "567f65fec4ef10dfab97ae71f26a27fd4d7fe7b8e3f90c8a58551c41ff3fb65b" dependencies = [ "bstr", "gix-trace", + "gix-validate", "home", "once_cell", "thiserror 2.0.12", @@ -4723,23 +4898,34 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04bdde120c29f1fc23a24d3e115aeeea3d60d8e65bab92cc5f9d90d9302eb952" +checksum = "7c396a2036920c69695f760a65e7f2677267ccf483f25046977d87e4cb2665f7" [[package]] name = "gix-url" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e09f97db3618fb8e473d7d97e77296b50aaee0ddcd6a867f07443e3e87391099" +checksum = "d096fb733ba6bd3f5403dba8bd72bdd8809fe2b347b57844040b8f49c93492d9" dependencies = [ "bstr", "gix-features", "gix-path", + "percent-encoding", "thiserror 2.0.12", "url", ] +[[package]] +name = "gix-validate" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77b9e00cacde5b51388d28ed746c493b18a6add1f19b5e01d686b3b9ece66d4d" +dependencies = [ + "bstr", + "thiserror 2.0.12", +] + [[package]] name = "gl_generator" version = "0.14.0" @@ -4806,9 +4992,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "global-hotkey" @@ -4866,9 +5052,9 @@ dependencies = [ [[package]] name = "glutin_wgl_sys" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e1951bbd9434a81aa496fe59ccc2235af3820d27b85f9314e279609211e2c" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" dependencies = [ "gl_generator", ] @@ -4881,7 +5067,7 @@ dependencies = [ "cosmic-text", "etagere", "lru", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "wgpu", ] @@ -4929,13 +5115,13 @@ dependencies = [ [[package]] name = "gpu-descriptor" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ "bitflags 2.9.1", "gpu-descriptor-types", - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -5050,16 +5236,16 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.2.0", + "http 1.3.1", "indexmap 2.9.0", "slab", "tokio", @@ -5069,9 +5255,9 @@ dependencies = [ [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -5111,15 +5297,15 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.11", + "ahash 0.8.12", "allocator-api2", ] [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", @@ -5132,7 +5318,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -5182,9 +5368,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" [[package]] name = "hex" @@ -5285,14 +5471,17 @@ dependencies = [ ] [[package]] -name = "hostname" -version = "0.3.1" +name = "hstr" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "71399f53a92ef72ee336a4b30201c6e944827e14e0af23204c291aad9c24cc85" dependencies = [ - "libc", - "match_cfg", - "winapi", + "hashbrown 0.14.5", + "new_debug_unreachable", + "once_cell", + "phf", + "rustc-hash 2.1.1", + "triomphe", ] [[package]] @@ -5314,9 +5503,9 @@ dependencies = [ [[package]] name = "http" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -5341,27 +5530,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.2.0", + "http 1.3.1", ] [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.2.0", + "futures-core", + "http 1.3.1", "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -5411,8 +5600,8 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.7", - "http 1.2.0", + "h2 0.4.10", + "http 1.3.1", "http-body 1.0.1", "httparse", "httpdate", @@ -5425,15 +5614,14 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.4" +version = "0.27.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6884a48c6826ec44f524c7456b163cebe9e55a18d7b5e307cb4f100371cc767" +checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" dependencies = [ - "futures-util", - "http 1.2.0", + "http 1.3.1", "hyper 1.6.0", "hyper-util", - "rustls 0.23.20", + "rustls 0.23.27", "rustls-pki-types", "tokio", "tokio-rustls", @@ -5442,9 +5630,9 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ "hyper 1.6.0", "hyper-util", @@ -5455,14 +5643,15 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710" +checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" dependencies = [ "bytes", "futures-channel", + "futures-core", "futures-util", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "hyper 1.6.0", "libc", @@ -5475,16 +5664,17 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core 0.61.2", ] [[package]] @@ -5537,7 +5727,7 @@ dependencies = [ "num-traits", "once_cell", "palette", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "smol_str", "thiserror 1.0.69", "web-time", @@ -5559,7 +5749,7 @@ dependencies = [ "futures", "iced_core", "log", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "tokio", "wasm-bindgen-futures", "wasm-timer", @@ -5581,7 +5771,7 @@ dependencies = [ "log", "once_cell", "raw-window-handle", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "thiserror 1.0.69", "unicode-segmentation", ] @@ -5661,7 +5851,7 @@ dependencies = [ "kurbo 0.10.4", "log", "resvg 0.42.0", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "softbuffer", "tiny-skia", ] @@ -5681,7 +5871,7 @@ dependencies = [ "log", "once_cell", "resvg 0.42.0", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "thiserror 1.0.69", "wgpu", ] @@ -5695,7 +5885,7 @@ dependencies = [ "iced_runtime", "num-traits", "once_cell", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "thiserror 1.0.69", "unicode-segmentation", ] @@ -5709,7 +5899,7 @@ dependencies = [ "iced_graphics", "iced_runtime", "log", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "thiserror 1.0.69", "tracing", "wasm-bindgen-futures", @@ -5731,21 +5921,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", - "yoke", + "potential_utf", + "yoke 0.8.0", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -5754,31 +5945,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -5786,67 +5957,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", - "yoke", + "yoke 0.8.0", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -5866,9 +6024,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -5916,9 +6074,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.5" +version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" dependencies = [ "bytemuck", "byteorder-lite", @@ -5939,12 +6097,12 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" dependencies = [ "byteorder-lite", - "quick-error 2.0.1", + "quick-error", ] [[package]] @@ -6019,7 +6177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "serde", ] @@ -6045,9 +6203,9 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "block-padding", "generic-array", @@ -6078,9 +6236,9 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "894148491d817cb36b6f778017b8ac46b17408d522dd90f539d677ea938362eb" +checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" dependencies = [ "doctest-file", "futures-core", @@ -6091,12 +6249,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "io-lifetimes" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" - [[package]] name = "ipconfig" version = "0.3.2" @@ -6111,9 +6263,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "ipnetwork" @@ -6133,6 +6285,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "is-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "is-wsl" version = "0.4.0" @@ -6178,33 +6342,37 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.1.15" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db69f08d4fb10524cacdb074c10b296299d71274ddbc830a8ee65666867002e9" +checksum = "c04ef77ae73f3cf50510712722f0c4e8b46f5aaa1bf5ffad2ae213e6495e78e5" dependencies = [ "jiff-tzdb-platform", "js-sys", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", "wasm-bindgen", "windows-sys 0.59.0", ] [[package]] name = "jiff-tzdb" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91335e575850c5c4c673b9bd467b0e025f164ca59d0564f69d0c2ee0ffad4653" +checksum = "c1283705eb0a21404d2bfd6eef2a7593d240bc42a0bdb39db0ad6fa2ec026524" [[package]] name = "jiff-tzdb-platform" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9835f0060a626fe59f160437bc725491a6af23133ea906500027d1bd2f8f4329" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" dependencies = [ "jiff-tzdb", ] @@ -6233,10 +6401,11 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.3", "libc", ] @@ -6328,7 +6497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading 0.8.6", + "libloading 0.8.8", "pkg-config", ] @@ -6340,9 +6509,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kqueue" -version = "1.0.8" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" dependencies = [ "kqueue-sys", "libc", @@ -6370,9 +6539,9 @@ dependencies = [ [[package]] name = "kurbo" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f" +checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c" dependencies = [ "arrayvec", "smallvec", @@ -6394,16 +6563,16 @@ dependencies = [ "wayland-backend", "wayland-client", "wayland-cursor", - "wayland-protocols 0.32.5", + "wayland-protocols", "wayland-protocols-misc", - "wayland-protocols-wlr 0.3.5", + "wayland-protocols-wlr", ] [[package]] name = "lazy-regex" -version = "3.3.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d8e41c97e6bc7ecb552016274b99fbb5d035e8de288c582d9b933af6677bfda" +checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" dependencies = [ "lazy-regex-proc_macros", "once_cell", @@ -6412,9 +6581,9 @@ dependencies = [ [[package]] name = "lazy-regex-proc_macros" -version = "3.3.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e1d8b05d672c53cb9c7b920bbba8783845ae4f0b076e02a3db1d02c81b4163" +checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" dependencies = [ "proc-macro2", "quote", @@ -6517,9 +6686,9 @@ dependencies = [ [[package]] name = "libfuzzer-sys" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" +checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" dependencies = [ "arbitrary", "cc", @@ -6551,19 +6720,19 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] name = "libm" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" @@ -6573,7 +6742,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.9.1", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.12", ] [[package]] @@ -6590,9 +6759,9 @@ dependencies = [ [[package]] name = "libssh2-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" +checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9" dependencies = [ "cc", "libc", @@ -6604,9 +6773,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.20" +version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" dependencies = [ "cc", "libc", @@ -6616,9 +6785,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" @@ -6627,10 +6796,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" [[package]] -name = "litemap" -version = "0.7.4" +name = "linux-raw-sys" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "litrs" @@ -6650,9 +6825,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "loom" @@ -6682,7 +6857,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -6730,12 +6905,6 @@ dependencies = [ "quote", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.1.0" @@ -6825,9 +6994,9 @@ dependencies = [ [[package]] name = "mendeleev" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f8dd6ec5207f7f69db7abb42466511394956dc85faf163de1fe393246c8b7e4" +checksum = "9f4cefcffb9afe2069e74baae496420299bf2a18a7d48b61cfc7482d7355db28" dependencies = [ "serde", ] @@ -6917,13 +7086,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6972,9 +7141,9 @@ dependencies = [ [[package]] name = "multimap" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "murmurhash32" @@ -7119,18 +7288,6 @@ dependencies = [ "libc", ] -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.9.1", - "cfg-if", - "cfg_aliases 0.1.1", - "libc", -] - [[package]] name = "nix" version = "0.29.0" @@ -7245,6 +7402,7 @@ dependencies = [ "num-integer", "num-traits", "rand 0.8.5", + "serde", ] [[package]] @@ -7358,7 +7516,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", "syn 2.0.101", @@ -7375,11 +7533,12 @@ dependencies = [ [[package]] name = "numbat" -version = "1.14.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5124c7a716bd197d4ad501237fa890771f69f38b34eb87f4514fdebf0cdcaf5b" +checksum = "5179fd3675cc6b9107c84a6838fdc94a6dfe769a259a9f51cc71f4ef67ffc1e7" dependencies = [ "codespan-reporting", + "compact_str 0.8.1", "heck 0.4.1", "indexmap 2.9.0", "itertools 0.12.1", @@ -7399,7 +7558,7 @@ dependencies = [ "strsim", "thiserror 1.0.69", "unicode-ident", - "unicode-width", + "unicode-width 0.1.14", "walkdir", ] @@ -7454,7 +7613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "libc", "objc2 0.5.2", "objc2-core-data", @@ -7471,6 +7630,8 @@ checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ "bitflags 2.9.1", "objc2 0.6.1", + "objc2-core-foundation", + "objc2-core-graphics", "objc2-foundation 0.3.1", ] @@ -7481,7 +7642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -7493,7 +7654,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -7505,7 +7666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -7521,13 +7682,26 @@ dependencies = [ "objc2 0.6.1", ] +[[package]] +name = "objc2-core-graphics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +dependencies = [ + "bitflags 2.9.1", + "dispatch2", + "objc2 0.6.1", + "objc2-core-foundation", + "objc2-io-surface", +] + [[package]] name = "objc2-core-image" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", @@ -7539,7 +7713,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-contacts", "objc2-foundation 0.2.2", @@ -7558,7 +7732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "dispatch", "libc", "objc2 0.5.2", @@ -7569,6 +7743,18 @@ name = "objc2-foundation" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +dependencies = [ + "bitflags 2.9.1", + "block2 0.6.1", + "objc2 0.6.1", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" dependencies = [ "bitflags 2.9.1", "objc2 0.6.1", @@ -7581,7 +7767,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -7594,7 +7780,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -7606,7 +7792,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", @@ -7629,7 +7815,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", @@ -7649,7 +7835,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -7661,7 +7847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.9.1", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -7678,9 +7864,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] @@ -7696,15 +7882,21 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "oneshot" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e296cf87e61c9cfc1a61c3c63a0f7f286ed4554e0e22be84e8a38e1d264a2a29" +checksum = "b4ce411919553d3f9fa53a0880544cda985a112117a0444d5ff1e870a893d6ea" [[package]] name = "opaque-debug" @@ -7714,9 +7906,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open" -version = "5.3.1" +version = "5.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ecd52f0b8d15c40ce4820aa251ed5de032e5d91fab27f7db2f40d42a8bdf69c" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" dependencies = [ "is-wsl", "libc", @@ -7725,24 +7917,24 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.4.1+3.4.0" +version = "300.5.0+3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" +checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" dependencies = [ "cc", "libc", @@ -7773,7 +7965,7 @@ checksum = "10a8a7f5f6ba7c1b286c2fbca0454eaba116f63bbe69ed250b642d36fbb04d80" dependencies = [ "async-trait", "bytes", - "http 1.2.0", + "http 1.3.1", "opentelemetry", ] @@ -7785,7 +7977,7 @@ checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76" dependencies = [ "async-trait", "futures-core", - "http 1.2.0", + "http 1.3.1", "opentelemetry", "opentelemetry-http", "opentelemetry-proto", @@ -7904,9 +8096,9 @@ dependencies = [ [[package]] name = "os_info" -version = "3.9.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ca711d8b83edbb00b44d504503cd247c9c0bd8b0fa2694f2a1a3d8165379ce" +checksum = "41fc863e2ca13dc2d5c34fb22ea4a588248ac14db929616ba65c45f21744b1e9" dependencies = [ "log", "serde", @@ -7925,15 +8117,9 @@ dependencies = [ [[package]] name = "outref" -version = "0.1.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" - -[[package]] -name = "outref" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" [[package]] name = "overload" @@ -7985,9 +8171,9 @@ dependencies = [ [[package]] name = "p384" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ "ecdsa", "elliptic-curve", @@ -8058,6 +8244,25 @@ dependencies = [ "system-deps", ] +[[package]] +name = "par-core" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757892557993c69e82f9de0f9051e87144278aa342f03bf53617bbf044554484" +dependencies = [ + "once_cell", +] + +[[package]] +name = "par-iter" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a5b20f31e9ba82bfcbbb54a67aa40be6cebec9f668ba5753be138f9523c531a" +dependencies = [ + "either", + "par-core", +] + [[package]] name = "parking" version = "2.2.1" @@ -8107,7 +8312,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.12", "smallvec", "windows-targets 0.52.6", ] @@ -8172,15 +8377,25 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "fixedbitset", + "fixedbitset 0.4.2", + "indexmap 2.9.0", +] + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset 0.5.7", "indexmap 2.9.0", ] [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", "phf_shared", @@ -8188,9 +8403,9 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", "rand 0.8.5", @@ -8198,9 +8413,9 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ "phf_generator", "phf_shared", @@ -8211,11 +8426,11 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher 0.3.11", + "siphasher 1.0.1", ] [[package]] @@ -8226,18 +8441,18 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", @@ -8246,9 +8461,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -8307,15 +8522,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" dependencies = [ "base64 0.22.1", "indexmap 2.9.0", @@ -8381,15 +8596,15 @@ dependencies = [ [[package]] name = "polling" -version = "3.7.4" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi 0.5.1", "pin-project-lite", - "rustix", + "rustix 1.0.7", "tracing", "windows-sys 0.59.0", ] @@ -8412,6 +8627,24 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -8420,9 +8653,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] @@ -8444,9 +8677,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", "syn 2.0.101", @@ -8482,11 +8715,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit 0.22.22", + "toml_edit 0.22.26", ] [[package]] @@ -8577,9 +8810,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", "prost-derive", @@ -8587,16 +8820,16 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck 0.5.0", - "itertools 0.13.0", + "itertools 0.14.0", "log", "multimap", "once_cell", - "petgraph", + "petgraph 0.7.1", "prettyplease", "prost", "prost-types", @@ -8607,12 +8840,12 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.101", @@ -8620,13 +8853,42 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" dependencies = [ "prost", ] +[[package]] +name = "psm" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" +dependencies = [ + "cc", +] + +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "qoi" version = "0.4.1" @@ -8636,12 +8898,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quick-error" version = "2.0.1" @@ -8668,9 +8924,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.36.2" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] @@ -8686,8 +8942,8 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.0", - "rustls 0.23.20", + "rustc-hash 2.1.1", + "rustls 0.23.27", "socket2", "thiserror 2.0.12", "tokio", @@ -8706,8 +8962,8 @@ dependencies = [ "lru-slab", "rand 0.9.1", "ring", - "rustc-hash 2.1.0", - "rustls 0.23.20", + "rustc-hash 2.1.1", + "rustls 0.23.27", "rustls-pki-types", "slab", "thiserror 2.0.12", @@ -8808,7 +9064,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -8832,9 +9088,9 @@ dependencies = [ [[package]] name = "range-alloc" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" +checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" [[package]] name = "rangemap" @@ -8879,14 +9135,14 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.11" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" +checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" dependencies = [ "avif-serialize", "imgref", "loop9", - "quick-error 2.0.1", + "quick-error", "rav1e", "rayon", "rgb", @@ -8954,9 +9210,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags 2.9.1", ] @@ -8967,25 +9223,36 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", "thiserror 1.0.69", ] [[package]] -name = "ref-cast" -version = "1.0.23" +name = "redox_users" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.12", +] + +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", @@ -9000,9 +9267,9 @@ checksum = "dc06e6b318142614e4a48bc725abbf08ff166694835c43c9dae5a9009704639a" dependencies = [ "allocator-api2", "bumpalo", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "log", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "smallvec", ] @@ -9058,13 +9325,9 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "resolv-conf" -version = "0.7.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error 1.2.3", -] +checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" [[package]] name = "resvg" @@ -9123,7 +9386,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -9156,7 +9419,7 @@ dependencies = [ "proc-macro2", "quote", "rinja_parser", - "rustc-hash 2.1.0", + "rustc-hash 2.1.1", "serde", "syn 2.0.101", ] @@ -9201,9 +9464,9 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rsa" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" +checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" dependencies = [ "const-oid", "digest", @@ -9236,9 +9499,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.5.0" +version = "8.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -9247,9 +9510,9 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.5.0" +version = "8.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" dependencies = [ "proc-macro2", "quote", @@ -9261,9 +9524,9 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "8.5.0" +version = "8.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" dependencies = [ "sha2", "walkdir", @@ -9313,9 +9576,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -9332,7 +9595,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.24", + "semver 1.0.26", ] [[package]] @@ -9346,14 +9609,27 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys 0.4.14", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] @@ -9366,22 +9642,22 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.102.8", "subtle", "zeroize", ] [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.103.3", "subtle", "zeroize", ] @@ -9410,11 +9686,12 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ "web-time", + "zeroize", ] [[package]] @@ -9424,7 +9701,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faa7dc7c991d9164e55bbf1558029eb5b84d32cc4d61a7df5b8641b2deedc4b3" dependencies = [ "futures", - "rustls 0.23.20", + "rustls 0.23.27", "socket2", "tokio", ] @@ -9441,10 +9718,21 @@ dependencies = [ ] [[package]] -name = "rustversion" -version = "1.0.18" +name = "rustls-webpki" +version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "rustybuzz" @@ -9480,16 +9768,22 @@ dependencies = [ "nix 0.27.1", "radix_trie", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.14", "utf8parse", "winapi", ] [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "ryu-js" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15" [[package]] name = "ryu_floating_decimal" @@ -9625,9 +9919,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -9635,9 +9929,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" +checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" [[package]] name = "semver" @@ -9650,9 +9944,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] @@ -9684,9 +9978,9 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.15" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" dependencies = [ "serde", ] @@ -9715,9 +10009,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "indexmap 2.9.0", "itoa", @@ -9728,9 +10022,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", @@ -9762,9 +10056,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ "base64 0.22.1", "chrono", @@ -9780,9 +10074,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ "darling", "proc-macro2", @@ -9813,9 +10107,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -9843,11 +10137,11 @@ dependencies = [ [[package]] name = "shellexpand" -version = "3.1.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" dependencies = [ - "dirs 5.0.1", + "dirs 6.0.0", ] [[package]] @@ -9858,9 +10152,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" dependencies = [ "libc", "signal-hook-registry", @@ -9868,9 +10162,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -9885,15 +10179,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "simd-abstraction" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" -dependencies = [ - "outref 0.1.0", -] - [[package]] name = "simd-adler32" version = "0.3.7" @@ -9906,7 +10191,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2bcf6c6e164e81bc7a5d49fc6988b3d515d9e8c07457d7b74ffb9324b9cd40" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "halfbrown", "ref-cast", "serde", @@ -9932,9 +10217,9 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "simplecss" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" dependencies = [ "log", ] @@ -10003,6 +10288,17 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + [[package]] name = "smithay-client-toolkit" version = "0.19.2" @@ -10018,14 +10314,14 @@ dependencies = [ "log", "memmap2 0.9.5", "pkg-config", - "rustix", + "rustix 0.38.44", "thiserror 1.0.69", "wayland-backend", "wayland-client", "wayland-csd-frame", "wayland-cursor", - "wayland-protocols 0.32.5", - "wayland-protocols-wlr 0.3.5", + "wayland-protocols", + "wayland-protocols-wlr", "wayland-scanner", "xkbcommon", "xkeysym", @@ -10053,9 +10349,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -10081,8 +10377,8 @@ dependencies = [ "objc2-foundation 0.2.2", "objc2-quartz-core", "raw-window-handle", - "redox_syscall 0.5.8", - "rustix", + "redox_syscall 0.5.12", + "rustix 0.38.44", "tiny-xlib", "wasm-bindgen", "wayland-backend", @@ -10095,17 +10391,16 @@ dependencies = [ [[package]] name = "sourcemap" -version = "9.1.2" +version = "9.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c4ea7042fd1a155ad95335b5d505ab00d5124ea0332a06c8390d200bb1a76a" +checksum = "bdee719193ae5c919a3ee43f64c2c0dd87f9b9a451d67918a2a5ec2e3c70561c" dependencies = [ - "base64-simd 0.7.0", + "base64-simd", "bitvec", "data-encoding", "debugid", "if_chain", - "rustc-hash 1.1.0", - "rustc_version 0.2.3", + "rustc-hash 2.1.1", "serde", "serde_json", "unicode-id-start", @@ -10149,6 +10444,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "stacker" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -10170,6 +10478,18 @@ dependencies = [ "float-cmp 0.9.0", ] +[[package]] +name = "string_enum" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9fe66b8ee349846ce2f9557a26b8f1e74843c4a13fb381f9a3d73617a5f956a" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common 1.0.0", + "syn 2.0.101", +] + [[package]] name = "stringcase" version = "0.4.0" @@ -10234,17 +10554,17 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "svg_fmt" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" [[package]] name = "svgtypes" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794de53cc48eaabeed0ab6a3404a65f40b3e38c067e4435883a65d2aa4ca000e" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" dependencies = [ - "kurbo 0.11.1", + "kurbo 0.11.2", "siphasher 1.0.1", ] @@ -10259,6 +10579,414 @@ dependencies = [ "zeno", ] +[[package]] +name = "swc_allocator" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6b926f0d94bbb34031fe5449428cfa1268cdc0b31158d6ad9c97e0fc1e79dd" +dependencies = [ + "allocator-api2", + "bumpalo", + "hashbrown 0.14.5", + "ptr_meta", + "rustc-hash 2.1.1", + "triomphe", +] + +[[package]] +name = "swc_atoms" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d7077ba879f95406459bc0c81f3141c529b34580bc64d7ab7bd15e7118a0391" +dependencies = [ + "hstr", + "once_cell", + "rustc-hash 2.1.1", + "serde", +] + +[[package]] +name = "swc_common" +version = "9.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56b6f5a8e5affa271b56757a93badee6f44defcd28f3ba106bb2603afe40d3d" +dependencies = [ + "anyhow", + "ast_node", + "better_scoped_tls", + "cfg-if", + "either", + "from_variant", + "new_debug_unreachable", + "num-bigint", + "once_cell", + "rustc-hash 2.1.1", + "serde", + "siphasher 0.3.11", + "sourcemap", + "swc_allocator", + "swc_atoms", + "swc_eq_ignore_macros", + "swc_visit", + "tracing", + "unicode-width 0.1.14", + "url", +] + +[[package]] +name = "swc_config" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01bfcbbdea182bdda93713aeecd997749ae324686bf7944f54d128e56be4ea9" +dependencies = [ + "anyhow", + "indexmap 2.9.0", + "serde", + "serde_json", + "swc_config_macro", +] + +[[package]] +name = "swc_config_macro" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2ebd37ef52a8555c8c9be78b694d64adcb5e3bc16c928f030d82f1d65fac57" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common 1.0.0", + "syn 2.0.101", +] + +[[package]] +name = "swc_ecma_ast" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0613d84468a6bb6d45d13c5a3368b37bd21f3067a089f69adac630dcb462a018" +dependencies = [ + "bitflags 2.9.1", + "is-macro", + "num-bigint", + "once_cell", + "phf", + "rustc-hash 2.1.1", + "scoped-tls", + "serde", + "string_enum", + "swc_atoms", + "swc_common", + "swc_visit", + "unicode-id-start", +] + +[[package]] +name = "swc_ecma_codegen" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b01b3de365a86b8f982cc162f257c82f84bda31d61084174a3be37e8ab15c0f4" +dependencies = [ + "ascii", + "compact_str 0.7.1", + "memchr", + "num-bigint", + "once_cell", + "regex", + "rustc-hash 2.1.1", + "serde", + "sourcemap", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_codegen_macros", + "tracing", +] + +[[package]] +name = "swc_ecma_codegen_macros" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e99e1931669a67c83e2c2b4375674f6901d1480994a76aa75b23f1389e6c5076" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common 1.0.0", + "syn 2.0.101", +] + +[[package]] +name = "swc_ecma_lexer" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d11c8e71901401b9aae2ece4946eeb7674b14b8301a53768afbbeeb0e48b599" +dependencies = [ + "arrayvec", + "bitflags 2.9.1", + "either", + "new_debug_unreachable", + "num-bigint", + "num-traits", + "phf", + "rustc-hash 2.1.1", + "serde", + "smallvec", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "tracing", + "typed-arena", +] + +[[package]] +name = "swc_ecma_loader" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb574d660c05f3483c984107452b386e45b95531bdb1253794077edc986f413" +dependencies = [ + "anyhow", + "pathdiff", + "rustc-hash 2.1.1", + "serde", + "swc_atoms", + "swc_common", + "tracing", +] + +[[package]] +name = "swc_ecma_parser" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250786944fbc05f6484eda9213df129ccfe17226ae9ad51b62fce2f72135dbee" +dependencies = [ + "arrayvec", + "bitflags 2.9.1", + "either", + "new_debug_unreachable", + "num-bigint", + "num-traits", + "phf", + "rustc-hash 2.1.1", + "serde", + "smallvec", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_lexer", + "tracing", + "typed-arena", +] + +[[package]] +name = "swc_ecma_transforms_base" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6856da3da598f4da001b7e4ce225ee8970bc9d5cbaafcaf580190cf0a6031ec5" +dependencies = [ + "better_scoped_tls", + "bitflags 2.9.1", + "indexmap 2.9.0", + "once_cell", + "par-core", + "phf", + "rustc-hash 2.1.1", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_utils", + "swc_ecma_visit", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_classes" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f84248f82bad599d250bbcd52cb4db6ff6409f48267fd6f001302a2e9716f80" +dependencies = [ + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6845dfb88569f3e8cd05901505916a8ebe98be3922f94769ca49f84e8ccec8f7" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common 1.0.0", + "syn 2.0.101", +] + +[[package]] +name = "swc_ecma_transforms_proposal" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193237e318421ef621c2b3958b4db174770c5280ef999f1878f2df93a2837ca6" +dependencies = [ + "either", + "rustc-hash 2.1.1", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_transforms_classes", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_react" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baae39c70229103a72090119887922fc5e32f934f5ca45c0423a5e65dac7e549" +dependencies = [ + "base64 0.22.1", + "dashmap", + "indexmap 2.9.0", + "once_cell", + "rustc-hash 2.1.1", + "serde", + "sha1", + "string_enum", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_config", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_transforms_base", + "swc_ecma_transforms_macros", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_transforms_typescript" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3c65e0b49f7e2a2bd92f1d89c9a404de27232ce00f6a4053f04bda446d50e5c" +dependencies = [ + "once_cell", + "rustc-hash 2.1.1", + "ryu-js", + "serde", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_transforms_react", + "swc_ecma_utils", + "swc_ecma_visit", +] + +[[package]] +name = "swc_ecma_utils" +version = "13.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ed837406d5dbbfbf5792b1dc90964245a0cf659753d4745fe177ffebe8598b9" +dependencies = [ + "indexmap 2.9.0", + "num_cpus", + "once_cell", + "par-core", + "par-iter", + "rustc-hash 2.1.1", + "ryu-js", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_visit", + "tracing", + "unicode-id", +] + +[[package]] +name = "swc_ecma_visit" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "249dc9eede1a4ad59a038f9cfd61ce67845bd2c1392ade3586d714e7181f3c1a" +dependencies = [ + "new_debug_unreachable", + "num-bigint", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_visit", + "tracing", +] + +[[package]] +name = "swc_eq_ignore_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96e15288bf385ab85eb83cff7f9e2d834348da58d0a31b33bdb572e66ee413e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "swc_macros_common" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e18fbfe83811ffae2bb23727e45829a0d19c6870bced7c0f545cc99ad248dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "swc_macros_common" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a509f56fca05b39ba6c15f3e58636c3924c78347d63853632ed2ffcb6f5a0ac7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "swc_visit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9138b6a36bbe76dd6753c4c0794f7e26480ea757bee499738bedbbb3ae3ec5f3" +dependencies = [ + "either", + "new_debug_unreachable", +] + +[[package]] +name = "swc_visit_macros" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92807d840959f39c60ce8a774a3f83e8193c658068e6d270dbe0a05e40e90b41" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "swc_macros_common 0.3.14", + "syn 2.0.101", +] + [[package]] name = "syn" version = "1.0.109" @@ -10301,9 +11029,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -10325,7 +11053,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3374191d43a934854e99a46cd47f8124369e690353e0f8db42769218d083690" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "windows-sys 0.59.0", ] @@ -10510,14 +11238,14 @@ checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" [[package]] name = "tempfile" -version = "3.14.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "cfg-if", "fastrand", + "getrandom 0.3.3", "once_cell", - "rustix", + "rustix 1.0.7", "windows-sys 0.59.0", ] @@ -10530,6 +11258,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "text_lines" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd5828de7deaa782e1dd713006ae96b3bee32d3279b79eb67ecf8072c059bcf" +dependencies = [ + "serde", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -10593,9 +11330,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.37" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -10610,15 +11347,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -10667,16 +11404,16 @@ checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" dependencies = [ "as-raw-xcb-connection", "ctor-lite", - "libloading 0.8.6", + "libloading 0.8.8", "pkg-config", "tracing", ] [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -10684,9 +11421,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -10699,14 +11436,14 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.3", + "mio 1.0.4", "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", @@ -10727,9 +11464,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", @@ -10750,11 +11487,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.20", + "rustls 0.23.27", "tokio", ] @@ -10783,16 +11520,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "futures-util", - "hashbrown 0.14.5", + "hashbrown 0.15.3", "pin-project-lite", "slab", "tokio", @@ -10813,21 +11550,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.19" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.22", + "toml_edit 0.22.26", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] @@ -10856,17 +11593,24 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.20", + "toml_write", + "winnow 0.7.10", ] +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + [[package]] name = "tonic" version = "0.12.3" @@ -10878,8 +11622,8 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.7", - "http 1.2.0", + "h2 0.4.10", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", @@ -10948,15 +11692,15 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" dependencies = [ "async-compression", "bitflags 2.9.1", "bytes", "futures-core", - "http 1.2.0", + "http 1.3.1", "http-body 1.0.1", "http-body-util", "pin-project-lite", @@ -11042,21 +11786,22 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.19.2" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d48a05076dd272615d03033bf04f480199f7d1b66a8ac64d75c625fc4a70c06b" +checksum = "eadd75f5002e2513eaa19b2365f533090cc3e93abd38788452d9ea85cff7b48a" dependencies = [ - "core-graphics 0.24.0", "crossbeam-channel", - "dirs 5.0.1", + "dirs 6.0.0", "libappindicator", "muda", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", + "objc2 0.6.1", + "objc2-app-kit 0.3.1", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.1", "once_cell", "png 0.17.16", - "thiserror 1.0.69", + "thiserror 2.0.12", "windows-sys 0.59.0", ] @@ -11070,7 +11815,17 @@ dependencies = [ "memchr", "nom 7.1.3", "once_cell", - "petgraph", + "petgraph 0.6.5", +] + +[[package]] +name = "triomphe" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +dependencies = [ + "serde", + "stable_deref_trait", ] [[package]] @@ -11108,6 +11863,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typed-path" version = "0.10.0" @@ -11116,15 +11877,15 @@ checksum = "41713888c5ccfd99979fcd1afd47b71652e331b3d4a0e19d30769e80fec76cce" [[package]] name = "typeid" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "uds_windows" @@ -11180,9 +11941,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bidi" @@ -11202,6 +11963,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" +[[package]] +name = "unicode-id" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" + [[package]] name = "unicode-id-start" version = "1.3.1" @@ -11210,9 +11977,9 @@ checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-linebreak" @@ -11250,6 +12017,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -11272,6 +12045,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "ureq" version = "2.12.1" @@ -11282,10 +12061,10 @@ dependencies = [ "flate2", "log", "once_cell", - "rustls 0.23.20", + "rustls 0.23.27", "rustls-pki-types", "url", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -11323,7 +12102,7 @@ dependencies = [ "flate2", "fontdb 0.18.0", "imagesize 0.12.0", - "kurbo 0.11.1", + "kurbo 0.11.2", "log", "pico-args", "roxmltree", @@ -11349,7 +12128,7 @@ dependencies = [ "data-url", "flate2", "imagesize 0.13.0", - "kurbo 0.11.1", + "kurbo 0.11.2", "log", "pico-args", "roxmltree", @@ -11367,12 +12146,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8-ranges" version = "1.0.5" @@ -11393,12 +12166,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.3.3", + "js-sys", "serde", + "wasm-bindgen", ] [[package]] @@ -11445,9 +12220,9 @@ dependencies = [ [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "value-trait" @@ -11469,9 +12244,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "9.0.2" +version = "9.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f25fc8f8f05df455c7941e87f093ad22522a9ff33d7a027774815acf6f0639" +checksum = "6b2bf58be11fc9414104c6d3a2e464163db5ef74b12296bda593cac37b6e4777" dependencies = [ "anyhow", "cargo_metadata", @@ -11484,9 +12259,9 @@ dependencies = [ [[package]] name = "vergen-gitcl" -version = "1.0.2" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0227006d09f98ab00ea69e9a5e055e676a813cfbed4232986176c86a6080b997" +checksum = "b9dfc1de6eb2e08a4ddf152f1b179529638bedc0ea95e6d667c014506377aefe" dependencies = [ "anyhow", "derive_builder", @@ -11498,9 +12273,9 @@ dependencies = [ [[package]] name = "vergen-lib" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0c767e6751c09fc85cde58722cf2f1007e80e4c8d5a4321fc90d83dc54ca147" +checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166" dependencies = [ "anyhow", "derive_builder", @@ -11509,9 +12284,9 @@ dependencies = [ [[package]] name = "vergen-pretty" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f0cedcb598e15120e748b19e97426e376be2ec27987570e11bbd350d0bdb09" +checksum = "7b6dba3d634a79e4a7af9eb2eb75d3629f81e3bbb8ae55d3ec2821acdeaac618" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -11533,9 +12308,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "virtue" -version = "0.0.13" +version = "0.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcc60c0624df774c82a0ef104151231d37da4962957d691c011c852b2473314" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" [[package]] name = "vsimd" @@ -11706,13 +12481,13 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.7" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" dependencies = [ "cc", "downcast-rs", - "rustix", + "rustix 0.38.44", "scoped-tls", "smallvec", "wayland-sys", @@ -11720,12 +12495,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.7" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" +checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" dependencies = [ "bitflags 2.9.1", - "rustix", + "rustix 0.38.44", "wayland-backend", "wayland-scanner", ] @@ -11743,32 +12518,20 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.7" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b08bc3aafdb0035e7fe0fdf17ba0c09c268732707dca4ae098f60cb28c9e4c" +checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" dependencies = [ - "rustix", + "rustix 0.38.44", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.31.2" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" -dependencies = [ - "bitflags 2.9.1", - "wayland-backend", - "wayland-client", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols" -version = "0.32.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" +checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" dependencies = [ "bitflags 2.9.1", "wayland-backend", @@ -11779,87 +12542,73 @@ dependencies = [ [[package]] name = "wayland-protocols-misc" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2e42969764e469a115d4bb1c16e9588ef8b75b127ba7a2c9ddf1e140b25ca7" +checksum = "635cf2968bd88599445b25a2eeef655d463bb04f9aed04e4bf8c2018f3d4fc41" dependencies = [ "bitflags 2.9.1", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.5", + "wayland-protocols", "wayland-scanner", ] [[package]] name = "wayland-protocols-plasma" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd" +checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" dependencies = [ "bitflags 2.9.1", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.5", + "wayland-protocols", "wayland-scanner", ] [[package]] name = "wayland-protocols-wlr" -version = "0.2.0" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" dependencies = [ "bitflags 2.9.1", "wayland-backend", "wayland-client", - "wayland-protocols 0.31.2", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols-wlr" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" -dependencies = [ - "bitflags 2.9.1", - "wayland-backend", - "wayland-client", - "wayland-protocols 0.32.5", + "wayland-protocols", "wayland-scanner", "wayland-server", ] [[package]] name = "wayland-scanner" -version = "0.31.5" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" dependencies = [ "proc-macro2", - "quick-xml 0.36.2", + "quick-xml 0.37.5", "quote", ] [[package]] name = "wayland-server" -version = "0.31.6" +version = "0.31.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89532cc712a2adb119eb4d09694b402576052254d0bb284f82ac1c47fb786ad" +checksum = "485dfb8ccf0daa0d34625d34e6ac15f99e550a7999b6fd88a0835ccd37655785" dependencies = [ "bitflags 2.9.1", "downcast-rs", - "io-lifetimes", - "rustix", + "rustix 0.38.44", "wayland-backend", "wayland-scanner", ] [[package]] name = "wayland-sys" -version = "0.31.5" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" dependencies = [ "dlib", "log", @@ -11894,34 +12643,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1814af4572856a29a2d29a56520e86fda994423043b70139ce98e5a32e0d91be" dependencies = [ "bytes", - "http 1.2.0", + "http 1.3.1", "thiserror 2.0.12", "url", ] [[package]] name = "webpki-root-certs" -version = "0.26.7" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd5da49bdf1f30054cfe0b8ce2958b8fbeb67c4d82c8967a598af481bef255c" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.0", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4" dependencies = [ "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "0.26.7" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.0", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" dependencies = [ "rustls-pki-types", ] [[package]] name = "weezl" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" [[package]] name = "wgpu" @@ -12023,7 +12790,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.6", + "libloading 0.8.8", "log", "metal 0.29.0", "naga 23.1.0", @@ -12068,7 +12835,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.6", + "libloading 0.8.8", "log", "metal 0.31.0", "naga 24.0.0", @@ -12122,38 +12889,38 @@ checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" dependencies = [ "either", "home", - "rustix", + "rustix 0.38.44", "winsafe", ] [[package]] name = "which" -version = "7.0.1" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a9e33648339dc1642b0e36e21b3385e6148e289226f657c809dee59df5028" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ "either", "env_home", - "rustix", + "rustix 1.0.7", "winsafe", ] [[package]] name = "whoami" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" dependencies = [ - "redox_syscall 0.5.8", + "redox_syscall 0.5.12", "wasite", "web-sys", ] [[package]] name = "widestring" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" [[package]] name = "winapi" @@ -12232,15 +12999,6 @@ dependencies = [ "windows-core 0.61.2", ] -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.58.0" @@ -12450,13 +13208,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows-threading" version = "0.1.0" @@ -12484,6 +13258,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -12502,6 +13282,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -12520,12 +13306,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -12544,6 +13342,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -12562,6 +13366,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -12580,6 +13390,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -12598,16 +13414,22 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winit" version = "0.30.99" source = "git+https://github.com/project-gauntlet/winit.git?rev=49690da86351375d2e2ebe8b891cdb90a019b996#49690da86351375d2e2ebe8b891cdb90a019b996" dependencies = [ - "ahash 0.8.11", + "ahash 0.8.12", "android-activity", "atomic-waker", "bitflags 2.9.1", - "block2", + "block2 0.5.1", "bytemuck", "calloop 0.13.0", "cfg_aliases 0.2.1", @@ -12629,7 +13451,7 @@ dependencies = [ "pin-project", "raw-window-handle", "redox_syscall 0.4.1", - "rustix", + "rustix 0.38.44", "sctk-adwaita", "smithay-client-toolkit", "smol_str", @@ -12639,7 +13461,7 @@ dependencies = [ "wasm-bindgen-futures", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.5", + "wayland-protocols", "wayland-protocols-plasma", "web-sys", "web-time", @@ -12660,9 +13482,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -12703,35 +13525,28 @@ dependencies = [ [[package]] name = "wl-clipboard-rs" -version = "0.8.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b41773911497b18ca8553c3daaf8ec9fe9819caf93d451d3055f69de028adb" +checksum = "8e5ff8d0e60065f549fafd9d6cb626203ea64a798186c80d8e7df4f8af56baeb" dependencies = [ - "derive-new", "libc", "log", - "nix 0.28.0", "os_pipe", + "rustix 0.38.44", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.12", "tree_magic_mini", "wayland-backend", "wayland-client", - "wayland-protocols 0.31.2", - "wayland-protocols-wlr 0.2.0", + "wayland-protocols", + "wayland-protocols-wlr", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wtf8" @@ -12768,9 +13583,9 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "libloading 0.8.6", + "libloading 0.8.8", "once_cell", - "rustix", + "rustix 0.38.44", "x11rb-protocol", ] @@ -12866,9 +13681,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" [[package]] name = "xmlwriter" @@ -12890,7 +13705,19 @@ checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", - "yoke-derive", + "yoke-derive 0.7.5", + "zerofrom", +] + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive 0.8.0", "zerofrom", ] @@ -12903,7 +13730,19 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.101", - "synstructure 0.13.1", + "synstructure 0.13.2", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "synstructure 0.13.2", ] [[package]] @@ -12950,7 +13789,7 @@ version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", "syn 2.0.101", @@ -12976,19 +13815,18 @@ checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", @@ -12997,23 +13835,23 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", "syn 2.0.101", - "synstructure 0.13.1", + "synstructure 0.13.2", ] [[package]] @@ -13037,21 +13875,32 @@ dependencies = [ ] [[package]] -name = "zerovec" -version = "0.10.4" +name = "zerotrie" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ - "yoke", + "displaydoc", + "yoke 0.8.0", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke 0.8.0", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", @@ -13060,27 +13909,27 @@ dependencies = [ [[package]] name = "zstd" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.2.1" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.13+zstd.1.5.6" +version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ "cc", "pkg-config", @@ -13129,7 +13978,7 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" dependencies = [ - "proc-macro-crate 3.2.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", "syn 2.0.101", diff --git a/rust/common_plugin_runtime/src/lib.rs b/rust/common_plugin_runtime/src/lib.rs index 945e469..ec50121 100644 --- a/rust/common_plugin_runtime/src/lib.rs +++ b/rust/common_plugin_runtime/src/lib.rs @@ -55,7 +55,7 @@ pub async fn send_message(side: JsMessageSide, send: &mut Sen Ok(()) } -pub async fn recv_message(side: JsMessageSide, recv: &mut RecvHalf) -> anyhow::Result { +pub async fn recv_message + Debug>(side: JsMessageSide, recv: &mut RecvHalf) -> anyhow::Result { tracing::trace!(side = debug(&side), "Waiting for next message..."); let message_id = recv.read_u32().await?; diff --git a/rust/plugin_runtime/src/deno.rs b/rust/plugin_runtime/src/deno.rs index aa8d9cd..4428489 100644 --- a/rust/plugin_runtime/src/deno.rs +++ b/rust/plugin_runtime/src/deno.rs @@ -26,7 +26,6 @@ use deno_resolver::npm::ManagedNpmResolver; use deno_runtime::deno_fs::RealFs; use deno_runtime::deno_io::Stdio; use deno_runtime::deno_io::StdioPipe; -use deno_runtime::deno_node::NodeExtInitServices; use deno_runtime::worker::MainWorker; use deno_runtime::worker::WorkerOptions; use deno_runtime::worker::WorkerServiceOptions; From 7e46395152796b3e5ce41313936547f7764d77b7 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 29 May 2025 19:22:13 +0200 Subject: [PATCH 11/91] Fix deno runtime not starting --- Cargo.toml | 1 + rust/plugin_runtime/Cargo.toml | 10 +++++----- rust/plugin_runtime/src/deno.rs | 17 +++++++++-------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2611c38..5c55a66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ members = [ "rust/component_model", "rust/scenario_runner", "rust/manifest_schema", + "rust/plugin_runtime", ] [workspace.package] diff --git a/rust/plugin_runtime/Cargo.toml b/rust/plugin_runtime/Cargo.toml index 21772dd..8e166d6 100644 --- a/rust/plugin_runtime/Cargo.toml +++ b/rust/plugin_runtime/Cargo.toml @@ -5,10 +5,10 @@ edition.workspace = true [dependencies] # workspaces -gauntlet-common = { path = "../common" } -gauntlet-component-model = { path = "../component_model" } -gauntlet-utils = { path = "../utils" } -gauntlet-common-plugin-runtime = { path = "../common_plugin_runtime" } +gauntlet-common.workspace = true +gauntlet-component-model.workspace = true +gauntlet-utils.workspace = true +gauntlet-common-plugin-runtime.workspace = true # shared anyhow.workspace = true @@ -31,7 +31,7 @@ interprocess.workspace = true # deno crates deno_core = { version = "0.347.0" } # https://github.com/denoland/deno/blob/v2.3.3 deno_runtime = { version = "0.213.0", features = ["transpile"] } -deno_error = { version = "*" } +deno_error = "*" deno_resolver = "*" sys_traits = "*" diff --git a/rust/plugin_runtime/src/deno.rs b/rust/plugin_runtime/src/deno.rs index 4428489..5a7a65d 100644 --- a/rust/plugin_runtime/src/deno.rs +++ b/rust/plugin_runtime/src/deno.rs @@ -373,6 +373,7 @@ deno_core::extension!( outer_handle: Handle }, state = |state, options| { + state.put(deno_runtime::ops::bootstrap::SnapshotOptions::default()); // workaround for deno requiring it, I assume by mistake state.put(options.event_receiver); state.put(options.plugin_data); state.put(options.component_model); @@ -524,16 +525,16 @@ pub async fn start_js_runtime( let init_url: ModuleSpecifier = "gauntlet:init".parse().expect("should be valid"); - let home_dir = PathBuf::from(init.home_dir); - let permissions_container = permissions_to_deno( &init.permissions, - &home_dir, + Path::new(&init.home_dir), Path::new(&init.plugin_data_dir), Path::new(&init.plugin_cache_dir), )?; - let gauntlet_esm = if cfg!(feature = "release") && !init.dev_plugin { + let prod = cfg!(feature = "release") && !init.dev_plugin; + + let gauntlet_esm = if prod { prod::gauntlet_esm::init() } else { dev::gauntlet_esm::init() @@ -544,12 +545,12 @@ pub async fn start_js_runtime( EventReceiver::new(event_stream), PluginData::new( init.plugin_id.clone(), - init.plugin_uuid.clone(), + init.plugin_uuid, init.plugin_cache_dir, init.plugin_data_dir, init.inline_view_entrypoint_id, init.entrypoint_names, - home_dir, + PathBuf::from(init.home_dir), ), ComponentModel::new(), api, @@ -565,13 +566,13 @@ pub async fn start_js_runtime( )); #[cfg(target_os = "macos")] - extensions.push(gauntlet_internal_macos::init_ops_and_esm()); + extensions.push(gauntlet_internal_macos::init()); #[cfg(target_os = "linux")] extensions.push(crate::plugins::applications::gauntlet_internal_linux::init()); #[cfg(target_os = "windows")] - extensions.push(crate::plugins::applications::gauntlet_internal_windows::init_ops_and_esm()); + extensions.push(crate::plugins::applications::gauntlet_internal_windows::init()); } let mut worker = MainWorker::bootstrap_from_options( From 1706840b8bcc7cb8b26a2c5b2f9a8a6ec446665e Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 30 May 2025 23:50:23 +0200 Subject: [PATCH 12/91] Migrate sqlite db migrations from sqlx migrations to rusqlite migrations --- Cargo.lock | 11 + rust/plugin_runtime/.gitignore | 1 - rust/plugin_runtime/src/deno.rs | 86 +------ rust/server/Cargo.toml | 2 +- rust/server/build.rs | 2 - rust/server/src/plugins/data_db_repository.rs | 144 +---------- .../plugins/data_db_repository/migrations.rs | 235 ++++++++++++++++++ .../10_frecency_fix_primary_keys.sql | 0 .../migrations}/11_settings_data.sql | 0 .../migrations}/12_settings_theme.sql | 0 .../13_remove_old_global_shortcut.sql | 0 .../14_migrate_to_rusqlite_migration.sql | 1 + .../migrations}/1_initial.sql | 0 .../migrations}/2_preferences.sql | 0 .../migrations}/3_plugin_description.sql | 0 .../migrations}/4_plugin_asset_data.sql | 0 .../migrations}/5_plugin_action_shortcuts.sql | 0 .../migrations}/6_plugin_type.sql | 0 .../migrations}/7_plugin_entrypoint_icon.sql | 0 .../migrations}/8_uuids.sql | 0 .../migrations}/9_frecency.sql | 0 rust/utils_macros/src/rusqlite.rs | 4 +- 22 files changed, 273 insertions(+), 213 deletions(-) delete mode 100644 rust/plugin_runtime/.gitignore create mode 100644 rust/server/src/plugins/data_db_repository/migrations.rs rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/10_frecency_fix_primary_keys.sql (100%) rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/11_settings_data.sql (100%) rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/12_settings_theme.sql (100%) rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/13_remove_old_global_shortcut.sql (100%) create mode 100644 rust/server/src/plugins/data_db_repository/migrations/14_migrate_to_rusqlite_migration.sql rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/1_initial.sql (100%) rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/2_preferences.sql (100%) rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/3_plugin_description.sql (100%) rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/4_plugin_asset_data.sql (100%) rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/5_plugin_action_shortcuts.sql (100%) rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/6_plugin_type.sql (100%) rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/7_plugin_entrypoint_icon.sql (100%) rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/8_uuids.sql (100%) rename rust/server/{db_migrations => src/plugins/data_db_repository/migrations}/9_frecency.sql (100%) diff --git a/Cargo.lock b/Cargo.lock index 70e2d54..c88d042 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4623,6 +4623,7 @@ dependencies = [ "open", "regex", "rusqlite", + "rusqlite_migration", "schemars", "serde", "serde_json", @@ -9497,6 +9498,16 @@ dependencies = [ "smallvec", ] +[[package]] +name = "rusqlite_migration" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad5c6b56a977875727f3d3a8d1baa8d875ed914719529ddc8b9330afaa8f0fda" +dependencies = [ + "log", + "rusqlite", +] + [[package]] name = "rust-embed" version = "8.7.2" diff --git a/rust/plugin_runtime/.gitignore b/rust/plugin_runtime/.gitignore deleted file mode 100644 index ea8c4bf..0000000 --- a/rust/plugin_runtime/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/rust/plugin_runtime/src/deno.rs b/rust/plugin_runtime/src/deno.rs index 5a7a65d..21643bb 100644 --- a/rust/plugin_runtime/src/deno.rs +++ b/rust/plugin_runtime/src/deno.rs @@ -121,81 +121,19 @@ pub enum GauntletJsError { Request(#[from] RequestError), } +#[rustfmt::skip] const MODULES: [(&str, &str); 11] = [ - ( - "gauntlet:init", - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/core/dist/init.js")), - ), - ( - "gauntlet:bridge/components", - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../js/bridge_build/dist/bridge-components.js" - )), - ), - ( - "gauntlet:bridge/hooks", - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../js/bridge_build/dist/bridge-hooks.js" - )), - ), - ( - "gauntlet:bridge/helpers", - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../js/bridge_build/dist/bridge-helpers.js" - )), - ), - ( - "gauntlet:bridge/core", - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../js/bridge_build/dist/bridge-core.js" - )), - ), - ( - "gauntlet:bridge/react", - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../js/bridge_build/dist/bridge-react.js" - )), - ), - ( - "gauntlet:bridge/react-jsx-runtime", - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../js/bridge_build/dist/bridge-react-jsx-runtime.js" - )), - ), - ( - "gauntlet:bridge/internal-all", - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../js/bridge_build/dist/bridge-internal-all.js" - )), - ), - ( - "gauntlet:bridge/internal-linux", - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../js/bridge_build/dist/bridge-internal-linux.js" - )), - ), - ( - "gauntlet:bridge/internal-macos", - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../js/bridge_build/dist/bridge-internal-macos.js" - )), - ), - ( - "gauntlet:bridge/internal-windows", - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../js/bridge_build/dist/bridge-internal-windows.js" - )), - ), + ("gauntlet:init", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/core/dist/init.js"))), + ("gauntlet:bridge/components", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/bridge_build/dist/bridge-components.js"))), + ("gauntlet:bridge/hooks", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/bridge_build/dist/bridge-hooks.js"))), + ("gauntlet:bridge/helpers", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/bridge_build/dist/bridge-helpers.js"))), + ("gauntlet:bridge/core", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/bridge_build/dist/bridge-core.js"))), + ("gauntlet:bridge/react", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/bridge_build/dist/bridge-react.js"))), + ("gauntlet:bridge/react-jsx-runtime", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/bridge_build/dist/bridge-react-jsx-runtime.js"))), + ("gauntlet:bridge/internal-all", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/bridge_build/dist/bridge-internal-all.js"))), + ("gauntlet:bridge/internal-linux", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/bridge_build/dist/bridge-internal-linux.js"))), + ("gauntlet:bridge/internal-macos", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/bridge_build/dist/bridge-internal-macos.js"))), + ("gauntlet:bridge/internal-windows", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../js/bridge_build/dist/bridge-internal-windows.js"))), ]; impl ModuleLoader for CustomModuleLoader { diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index 6bbb13f..cd3501c 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -36,7 +36,7 @@ tantivy = "0.22" git2 = { version = "0.19", features = ["vendored-libgit2", "vendored-openssl"] } tempfile = "3" rusqlite = { version = "0.34.0", features = ["serde_json"] } - +rusqlite_migration = "2.0.0" include_dir = "0.7" open = "5" uuid = "1.8" diff --git a/rust/server/build.rs b/rust/server/build.rs index 560f264..aaac632 100644 --- a/rust/server/build.rs +++ b/rust/server/build.rs @@ -3,8 +3,6 @@ use vergen_gitcl::Emitter; use vergen_gitcl::GitclBuilder; fn main() -> Result<(), Box> { - println!("cargo:rerun-if-changed=db_migrations"); - let gitcl = GitclBuilder::all_git()?; let cargo = CargoBuilder::default() .debug(true) diff --git a/rust/server/src/plugins/data_db_repository.rs b/rust/server/src/plugins/data_db_repository.rs index b984263..23c7494 100644 --- a/rust/server/src/plugins/data_db_repository.rs +++ b/rust/server/src/plugins/data_db_repository.rs @@ -1,3 +1,5 @@ +mod migrations; + use std::collections::HashMap; use std::collections::HashSet; use std::sync::Arc; @@ -18,11 +20,10 @@ use serde::Serialize; use uuid::Uuid; use crate::model::ActionShortcutKey; +use crate::plugins::data_db_repository::migrations::setup_migrator; use crate::plugins::frecency::FrecencyItemStats; use crate::plugins::frecency::FrecencyMetaParams; -// static MIGRATOR: Migrator = sqlx::migrate!("./db_migrations"); - #[derive(Clone)] pub struct DataDbRepository { connection: Arc>, @@ -385,140 +386,17 @@ impl DataDbRepository { std::fs::create_dir_all(&data_db_file.parent().unwrap()).context("Unable to create data directory")?; - let connection = Connection::open(&data_db_file).context("Unable to open database connection")?; + let mut connection = Connection::open(&data_db_file).context("Unable to open database connection")?; - let db_repository = Self { + setup_migrator() + .to_latest(&mut connection) + .context("Unable apply database migration")?; + + Ok(Self { connection: Arc::new(Mutex::new(connection)), - }; - - // let _ = db_repository.migrate_global_shortcut_to_settings().await; - // - // db_repository.run_migrations().await?; - // - // db_repository.apply_uuid_default_value().await?; - // db_repository.remove_legacy_bundled_plugins().await?; - - Ok(db_repository) + }) } - // async fn run_migrations(&self) -> anyhow::Result<()> { - // MIGRATOR - // .run(&self.connection) - // .await - // .context("Unable apply database migration")?; - // - // Ok(()) - // } - // - // async fn apply_uuid_default_value(&self) -> anyhow::Result<()> { - // // language=SQLite - // let mut stream = self - // .connection - // .fetch(sqlx::query("SELECT id FROM plugin WHERE uuid IS NULL")); - // while let Some(row) = stream.next().await { - // let row = row?; - // let id: &str = row.get("id"); - // - // // language=SQLite - // sqlx::query("UPDATE plugin SET uuid = ?1 WHERE id = ?2") - // .bind(Uuid::new_v4().to_string()) - // .bind(id) - // .execute(&self.connection) - // .await?; - // } - // - // // language=SQLite - // let mut stream = self - // .connection - // .fetch(sqlx::query("SELECT id FROM plugin_entrypoint WHERE uuid IS NULL")); - // while let Some(row) = stream.next().await { - // let row = row?; - // let id: &str = row.get("id"); - // - // // language=SQLite - // sqlx::query("UPDATE plugin_entrypoint SET uuid = ?1 WHERE id = ?2") - // .bind(Uuid::new_v4().to_string()) - // .bind(id) - // .execute(&self.connection) - // .await?; - // } - // - // Ok(()) - // } - // - // async fn remove_legacy_bundled_plugins(&self) -> anyhow::Result<()> { - // self.remove_plugin("builtin://applications").await?; - // self.remove_plugin("builtin://calculator").await?; - // self.remove_plugin("builtin://settings").await?; - // - // Ok(()) - // } - // - // async fn migrate_global_shortcut_to_settings(&self) -> anyhow::Result<()> { - // #[derive(RusqliteModel)] - // struct DbSettingsDataOldContainer { - // #[rusqlite(json)] - // pub global_shortcut: DbSettingsGlobalShortcutOldData, - // pub settings: Option>, - // } - // - // #[derive(Debug, Deserialize, Serialize)] - // pub struct DbSettingsGlobalShortcutOldData { - // pub physical_key: String, - // pub modifier_shift: bool, - // pub modifier_control: bool, - // pub modifier_alt: bool, - // pub modifier_meta: bool, - // #[serde(default)] - // pub unset: bool, - // #[serde(default)] - // pub error: Option, - // } - // - // // language=SQLite - // let mut data = sqlx::query_as::<_, DbSettingsDataOldContainer>("SELECT * FROM settings_data") - // .fetch_one(&self.connection) - // .await?; - // - // let shortcut_data = &data.global_shortcut; - // - // let shortcut = if shortcut_data.unset { - // None - // } else { - // Some(DbSettingsGlobalShortcutData { - // shortcut: DbSettingsShortcut { - // physical_key: shortcut_data.physical_key.clone(), - // modifier_shift: shortcut_data.modifier_shift, - // modifier_control: shortcut_data.modifier_control, - // modifier_alt: shortcut_data.modifier_alt, - // modifier_meta: shortcut_data.modifier_meta, - // }, - // error: None, - // }) - // }; - // - // if let Some(settings) = &mut data.settings { - // settings.global_shortcut = shortcut; - // } - // - // // language=SQLite - // let sql = r#" - // INSERT INTO settings_data (id, global_shortcut, settings) - // VALUES(?1, ?2, ?3) - // ON CONFLICT (id) - // DO UPDATE SET settings = ?3 - // "#; - // - // sqlx::query(sql) - // .bind(SETTINGS_DATA_ID) - // .bind(Json(data.global_shortcut)) - // .bind(data.settings) - // .execute(&self.connection) - // .await?; - // - // Ok(()) - // } - pub fn list_plugins(&self) -> anyhow::Result> { // language=SQLite let query = "SELECT * FROM plugin"; @@ -1448,7 +1326,7 @@ pub fn db_plugin_type_from_str(value: &str) -> DbPluginType { } } -trait RusqliteFromRow { +pub trait RusqliteFromRow { fn from_row(row: &Row<'_>) -> rusqlite::Result where Self: Sized; diff --git a/rust/server/src/plugins/data_db_repository/migrations.rs b/rust/server/src/plugins/data_db_repository/migrations.rs new file mode 100644 index 0000000..95e6b5b --- /dev/null +++ b/rust/server/src/plugins/data_db_repository/migrations.rs @@ -0,0 +1,235 @@ +use rusqlite::Transaction; +use rusqlite::named_params; +use rusqlite_migration::HookError; +use rusqlite_migration::HookResult; +use rusqlite_migration::M; +use rusqlite_migration::MigrationHook; +use rusqlite_migration::Migrations; +use serde::Deserialize; +use serde::Serialize; +use uuid::Uuid; + +use crate::plugins::data_db_repository::DbSettings; +use crate::plugins::data_db_repository::DbSettingsGlobalShortcutData; +use crate::plugins::data_db_repository::DbSettingsShortcut; +use crate::plugins::data_db_repository::RusqliteFromRow; +use crate::plugins::data_db_repository::SETTINGS_DATA_ID; + +fn migration_needed(conn: &Transaction, description: &str) -> Result { + let result = conn.query_row( + "SELECT 1 FROM _sqlx_migrations WHERE description = :description", + named_params! { + ":description": description + }, + |_| Ok(()), + ); + + let needed = match result { + Ok(()) => false, // already applied by previous system + Err(err) => { + match err { + rusqlite::Error::QueryReturnedNoRows => { + // migration not applied by previous system, but db exists + true + } + _ => { + // possibly, no _sqlx_migrations table, meaning database is new, + // or migration to new system is already done which shouldn't happen because migration was already applied + true + } + } + } + }; + + Ok(needed) +} + +fn legacy_migration(legacy_description: &'static str, sql: &'static str) -> impl MigrationHook + 'static { + move |tx| -> HookResult { + if migration_needed(tx, legacy_description)? { + tx.execute_batch(sql)?; + } + + Ok(()) + } +} + +fn legacy_migration_fn( + legacy_description: &'static str, + hook: impl MigrationHook + Clone + 'static, +) -> impl MigrationHook + 'static { + move |tx| { + if migration_needed(&tx, legacy_description)? { + hook(&tx)?; + } + + Ok(()) + } +} + +fn migrate_global_shortcut_to_settings(tx: &Transaction) -> HookResult { + #[derive(RusqliteFromRow)] + struct DbSettingsDataOldContainer { + #[rusqlite(json)] + pub global_shortcut: DbSettingsGlobalShortcutOldData, + #[rusqlite(json)] + pub settings: Option, + } + + #[derive(Debug, Deserialize, Serialize)] + pub struct DbSettingsGlobalShortcutOldData { + pub physical_key: String, + pub modifier_shift: bool, + pub modifier_control: bool, + pub modifier_alt: bool, + pub modifier_meta: bool, + #[serde(default)] + pub unset: bool, + #[serde(default)] + pub error: Option, + } + + // language=SQLite + let query = "SELECT * FROM settings_data"; + + let mut data = tx.prepare(query)?.query_row([], DbSettingsDataOldContainer::from_row)?; + + let shortcut_data = &data.global_shortcut; + + let shortcut = if shortcut_data.unset { + None + } else { + Some(DbSettingsGlobalShortcutData { + shortcut: DbSettingsShortcut { + physical_key: shortcut_data.physical_key.clone(), + modifier_shift: shortcut_data.modifier_shift, + modifier_control: shortcut_data.modifier_control, + modifier_alt: shortcut_data.modifier_alt, + modifier_meta: shortcut_data.modifier_meta, + }, + error: None, + }) + }; + + if let Some(settings) = &mut data.settings { + settings.global_shortcut = shortcut; + } + + // language=SQLite + let query = r#" + INSERT INTO settings_data (id, global_shortcut, settings) + VALUES(:id, :global_shortcut, :settings) + ON CONFLICT (id) + DO UPDATE SET settings = :settings + "#; + + tx.execute( + query, + named_params! { + ":id": SETTINGS_DATA_ID, + ":global_shortcut": serde_json::to_value(data.global_shortcut).map_err(|err| HookError::Hook(format!("{:?}", err)))?, + ":settings": serde_json::to_value(data.settings).map_err(|err| HookError::Hook(format!("{:?}", err)))?, + }, + )?; + + Ok(()) +} + +fn remove_legacy_bundled_plugins(tx: &Transaction) -> HookResult { + let remove_plugin = |plugin_id: &str| -> HookResult { + // language=SQLite + let query = "DELETE FROM plugin WHERE id = :id"; + + tx.execute( + query, + named_params! { + ":id": plugin_id + }, + )?; + + Ok(()) + }; + + remove_plugin("builtin://applications")?; + remove_plugin("builtin://calculator")?; + remove_plugin("builtin://settings")?; + + Ok(()) +} + +fn apply_uuid_default_value(tx: &Transaction) -> HookResult { + #[derive(RusqliteFromRow)] + struct DbResultId { + pub id: String, + } + + // language=SQLite + let query = "SELECT id FROM plugin WHERE uuid IS NULL"; + + let results = tx + .prepare(query)? + .query_and_then([], DbResultId::from_row)? + .collect::, _>>()?; + + for result in results { + // language=SQLite + let query = "UPDATE plugin SET uuid = :uuid WHERE id = :id"; + + tx.execute( + query, + named_params! { + ":id": result.id, + ":uuid": Uuid::new_v4().to_string() + }, + )?; + } + + // language=SQLite + let query = "SELECT id FROM plugin_entrypoint WHERE uuid IS NULL"; + + let results = tx + .prepare(query)? + .query_and_then([], DbResultId::from_row)? + .collect::, _>>()?; + + for result in results { + // language=SQLite + let query = "UPDATE plugin_entrypoint SET uuid = :uuid WHERE id = :id"; + + tx.execute( + query, + named_params! { + ":id": result.id, + ":uuid": Uuid::new_v4().to_string() + }, + )?; + } + + Ok(()) +} + +#[rustfmt::skip] +pub fn setup_migrator() -> Migrations<'static> { + Migrations::new(vec![ + M::up_with_hook("-- 1", legacy_migration("initial", include_str!("migrations/1_initial.sql"))), + M::up_with_hook("-- 2", legacy_migration("preferences", include_str!("migrations/2_preferences.sql"))), + M::up_with_hook("-- 3", legacy_migration("plugin description", include_str!("migrations/3_plugin_description.sql"))), + M::up_with_hook("-- 4", legacy_migration("plugin asset data", include_str!("migrations/4_plugin_asset_data.sql"))), + M::up_with_hook("-- 5", legacy_migration("plugin action shortcuts", include_str!("migrations/5_plugin_action_shortcuts.sql"))), + M::up_with_hook("-- 6", legacy_migration("plugin type", include_str!("migrations/6_plugin_type.sql"))), + M::up_with_hook("-- 7", legacy_migration("plugin entrypoint icon", include_str!("migrations/7_plugin_entrypoint_icon.sql"))), + M::up_with_hook("-- 8", legacy_migration("uuids", include_str!("migrations/8_uuids.sql"))), + M::up_with_hook("-- 9", apply_uuid_default_value), + M::up_with_hook("-- 10", legacy_migration("frecency", include_str!("migrations/9_frecency.sql"))), + M::up_with_hook("-- 11", legacy_migration("frecency fix primary keys", include_str!("migrations/10_frecency_fix_primary_keys.sql"))), + M::up_with_hook("-- 12", legacy_migration("settings data", include_str!("migrations/11_settings_data.sql"))), + M::up_with_hook("-- 13", remove_legacy_bundled_plugins), + M::up_with_hook("-- 14", legacy_migration("settings theme", include_str!("migrations/12_settings_theme.sql"))), + M::up_with_hook("-- 15", legacy_migration_fn("remove old global shortcut", |tx| { + let _ = migrate_global_shortcut_to_settings(tx); + Ok(()) + })), + M::up_with_hook("-- 16", legacy_migration("remove old global shortcut", include_str!("migrations/13_remove_old_global_shortcut.sql"))), + M::up(include_str!("migrations/14_migrate_to_rusqlite_migration.sql")) + ]) +} diff --git a/rust/server/db_migrations/10_frecency_fix_primary_keys.sql b/rust/server/src/plugins/data_db_repository/migrations/10_frecency_fix_primary_keys.sql similarity index 100% rename from rust/server/db_migrations/10_frecency_fix_primary_keys.sql rename to rust/server/src/plugins/data_db_repository/migrations/10_frecency_fix_primary_keys.sql diff --git a/rust/server/db_migrations/11_settings_data.sql b/rust/server/src/plugins/data_db_repository/migrations/11_settings_data.sql similarity index 100% rename from rust/server/db_migrations/11_settings_data.sql rename to rust/server/src/plugins/data_db_repository/migrations/11_settings_data.sql diff --git a/rust/server/db_migrations/12_settings_theme.sql b/rust/server/src/plugins/data_db_repository/migrations/12_settings_theme.sql similarity index 100% rename from rust/server/db_migrations/12_settings_theme.sql rename to rust/server/src/plugins/data_db_repository/migrations/12_settings_theme.sql diff --git a/rust/server/db_migrations/13_remove_old_global_shortcut.sql b/rust/server/src/plugins/data_db_repository/migrations/13_remove_old_global_shortcut.sql similarity index 100% rename from rust/server/db_migrations/13_remove_old_global_shortcut.sql rename to rust/server/src/plugins/data_db_repository/migrations/13_remove_old_global_shortcut.sql diff --git a/rust/server/src/plugins/data_db_repository/migrations/14_migrate_to_rusqlite_migration.sql b/rust/server/src/plugins/data_db_repository/migrations/14_migrate_to_rusqlite_migration.sql new file mode 100644 index 0000000..16649db --- /dev/null +++ b/rust/server/src/plugins/data_db_repository/migrations/14_migrate_to_rusqlite_migration.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS _sqlx_migrations; diff --git a/rust/server/db_migrations/1_initial.sql b/rust/server/src/plugins/data_db_repository/migrations/1_initial.sql similarity index 100% rename from rust/server/db_migrations/1_initial.sql rename to rust/server/src/plugins/data_db_repository/migrations/1_initial.sql diff --git a/rust/server/db_migrations/2_preferences.sql b/rust/server/src/plugins/data_db_repository/migrations/2_preferences.sql similarity index 100% rename from rust/server/db_migrations/2_preferences.sql rename to rust/server/src/plugins/data_db_repository/migrations/2_preferences.sql diff --git a/rust/server/db_migrations/3_plugin_description.sql b/rust/server/src/plugins/data_db_repository/migrations/3_plugin_description.sql similarity index 100% rename from rust/server/db_migrations/3_plugin_description.sql rename to rust/server/src/plugins/data_db_repository/migrations/3_plugin_description.sql diff --git a/rust/server/db_migrations/4_plugin_asset_data.sql b/rust/server/src/plugins/data_db_repository/migrations/4_plugin_asset_data.sql similarity index 100% rename from rust/server/db_migrations/4_plugin_asset_data.sql rename to rust/server/src/plugins/data_db_repository/migrations/4_plugin_asset_data.sql diff --git a/rust/server/db_migrations/5_plugin_action_shortcuts.sql b/rust/server/src/plugins/data_db_repository/migrations/5_plugin_action_shortcuts.sql similarity index 100% rename from rust/server/db_migrations/5_plugin_action_shortcuts.sql rename to rust/server/src/plugins/data_db_repository/migrations/5_plugin_action_shortcuts.sql diff --git a/rust/server/db_migrations/6_plugin_type.sql b/rust/server/src/plugins/data_db_repository/migrations/6_plugin_type.sql similarity index 100% rename from rust/server/db_migrations/6_plugin_type.sql rename to rust/server/src/plugins/data_db_repository/migrations/6_plugin_type.sql diff --git a/rust/server/db_migrations/7_plugin_entrypoint_icon.sql b/rust/server/src/plugins/data_db_repository/migrations/7_plugin_entrypoint_icon.sql similarity index 100% rename from rust/server/db_migrations/7_plugin_entrypoint_icon.sql rename to rust/server/src/plugins/data_db_repository/migrations/7_plugin_entrypoint_icon.sql diff --git a/rust/server/db_migrations/8_uuids.sql b/rust/server/src/plugins/data_db_repository/migrations/8_uuids.sql similarity index 100% rename from rust/server/db_migrations/8_uuids.sql rename to rust/server/src/plugins/data_db_repository/migrations/8_uuids.sql diff --git a/rust/server/db_migrations/9_frecency.sql b/rust/server/src/plugins/data_db_repository/migrations/9_frecency.sql similarity index 100% rename from rust/server/db_migrations/9_frecency.sql rename to rust/server/src/plugins/data_db_repository/migrations/9_frecency.sql diff --git a/rust/utils_macros/src/rusqlite.rs b/rust/utils_macros/src/rusqlite.rs index 6dfd492..940c48a 100644 --- a/rust/utils_macros/src/rusqlite.rs +++ b/rust/utils_macros/src/rusqlite.rs @@ -57,8 +57,8 @@ pub fn derive_rusqlite(item: TokenStream) -> TokenStream { .collect(); let result = quote! { - impl RusqliteFromRow for #struct_name { - fn from_row(row: &Row<'_>) -> rusqlite::Result { + impl crate::plugins::data_db_repository::RusqliteFromRow for #struct_name { + fn from_row(row: &rusqlite::Row<'_>) -> rusqlite::Result { Ok(Self { #(#output)* }) From 30963ffd73a5c5ae59a8e16707cbb208b73ae3d1 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 31 May 2025 19:23:23 +0200 Subject: [PATCH 13/91] Fix nix hash mismatch. Add nix build to github actions --- .github/workflows/format.yaml | 4 ++-- .github/workflows/nix.yaml | 11 +++++++++++ nix/overlay.nix | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/nix.yaml diff --git a/.github/workflows/format.yaml b/.github/workflows/format.yaml index ed2bfbc..0aa561f 100644 --- a/.github/workflows/format.yaml +++ b/.github/workflows/format.yaml @@ -1,4 +1,4 @@ -name: Format +name: format on: [push, pull_request] jobs: all: @@ -9,4 +9,4 @@ jobs: components: rustfmt - uses: actions/checkout@v4 - name: Check format - run: cargo +nightly fmt --all -- --check --verbose \ No newline at end of file + run: cargo +nightly fmt --all -- --check --verbose diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml new file mode 100644 index 0000000..9793897 --- /dev/null +++ b/.github/workflows/nix.yaml @@ -0,0 +1,11 @@ +name: nix build +on: [push, pull_request] +jobs: + all: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v25 + with: + nix_path: nixpkgs=channel:nixos-unstable + - run: nix-build diff --git a/nix/overlay.nix b/nix/overlay.nix index eff88c2..be282c4 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -6,7 +6,7 @@ flake.overlays.default = final: _: let version = "v${builtins.readFile ./../VERSION}"; # These must be updated following the instructions in ./nix/README.md when dependencies are updated or the version bumped - npmDepsHash = "sha256-Ty2mtoftvopyTKryENs+7HW00s0BGoHdgYvpIePieV0="; + npmDepsHash = "sha256-AAnbT3B68U+dTqTfCA4eYFo+VQBcY4LTj98Nnq+sIes="; RUSTY_V8_ARCHIVE = fetchRustyV8 "130.0.2" { aarch64-darwin = "sha256-aWZ/4Q4Wttx37xOdBmTCPGP+eYGhr4CM1UkYq8pC7Qs="; aarch64-linux = "sha256-p9+tHmKIM5wBABubHIAstpwfzO19ypPzOuaV4b6loCU="; From 901f8fdfa37de1802a5230687bde0ea154964f25 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 31 May 2025 19:34:22 +0200 Subject: [PATCH 14/91] Explicitly add bundled rusqlite feature --- rust/server/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index cd3501c..441538b 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -35,7 +35,7 @@ toml.workspace = true tantivy = "0.22" git2 = { version = "0.19", features = ["vendored-libgit2", "vendored-openssl"] } tempfile = "3" -rusqlite = { version = "0.34.0", features = ["serde_json"] } +rusqlite = { version = "0.34.0", features = ["serde_json", "bundled"] } rusqlite_migration = "2.0.0" include_dir = "0.7" open = "5" From 747cb9435df9c761962a73ec4e30b1c4a9388766 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 31 May 2025 20:26:23 +0200 Subject: [PATCH 15/91] Fix windows build --- rust/client/src/ui/mod.rs | 23 ++++++++++++++----- rust/plugin_runtime/src/plugin_data.rs | 1 + .../src/plugins/applications/windows.rs | 15 ++++++------ rust/server/src/plugins/js.rs | 15 ++++++++---- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index e63b98e..f35c8ae 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -73,7 +73,6 @@ use iced::window::Mode; use iced::window::Position; use iced::window::Screenshot; use iced_fonts::BOOTSTRAP_FONT_BYTES; -use tokio::runtime::Handle; use tokio::sync::RwLock as TokioRwLock; use crate::model::UiViewEvent; @@ -130,9 +129,10 @@ pub struct AppModel { main_window_id: window::Id, focused: bool, opened: bool, + #[cfg(target_os = "linux")] wayland: bool, #[cfg(any(target_os = "macos", target_os = "windows"))] - tray_icon: tray_icon::TrayIcon, + _tray_icon: tray_icon::TrayIcon, theme: GauntletComplexTheme, window_position_mode: WindowPositionMode, close_on_unfocus: bool, @@ -494,7 +494,15 @@ fn run_non_wayland( }) .subscription(subscription) .theme(|state, _| state.theme.clone()) - .run_with(move || new(frontend_receiver, backend_sender, false, minimized))?; + .run_with(move || { + new( + frontend_receiver, + backend_sender, + #[cfg(target_os = "linux")] + false, + minimized, + ) + })?; Ok(()) } @@ -526,7 +534,7 @@ fn wayland_remove_id_info(_state: &mut AppModel, _id: window::Id) {} fn new( frontend_receiver: RequestReceiver, backend_sender: RequestSender, - wayland: bool, + #[cfg(target_os = "linux")] wayland: bool, minimized: bool, ) -> (AppModel, Task) { let backend_api = BackendForFrontendApiProxy::new(backend_sender); @@ -701,9 +709,10 @@ fn new( main_window_id, focused: false, opened: !minimized, + #[cfg(target_os = "linux")] wayland, #[cfg(any(target_os = "macos", target_os = "windows"))] - tray_icon: sys_tray::create_tray(), + _tray_icon: sys_tray::create_tray(), theme, window_position_mode: setup_data.window_position_mode, close_on_unfocus: setup_data.close_on_unfocus, @@ -2236,6 +2245,7 @@ fn subscription(state: &AppModel) -> Subscription { struct RequestLoop; struct GlobalShortcutListener; + #[cfg(target_os = "linux")] struct X11ActiveWindowListener; let events_subscription = event::listen_with(|event, status, window_id| { @@ -2253,6 +2263,7 @@ fn subscription(state: &AppModel) -> Subscription { } }); + #[allow(unused_mut)] let mut subscriptions = vec![ Subscription::run_with_id( std::any::TypeId::of::(), @@ -2285,7 +2296,7 @@ fn subscription(state: &AppModel) -> Subscription { #[cfg(target_os = "linux")] if !state.wayland { - let handle = Handle::current(); + let handle = tokio::runtime::Handle::current(); let subscription = Subscription::run_with_id( std::any::TypeId::of::(), diff --git a/rust/plugin_runtime/src/plugin_data.rs b/rust/plugin_runtime/src/plugin_data.rs index ee31c50..328ae18 100644 --- a/rust/plugin_runtime/src/plugin_data.rs +++ b/rust/plugin_runtime/src/plugin_data.rs @@ -65,6 +65,7 @@ impl PluginData { &self.entrypoint_names } + #[allow(unused)] pub fn home_dir(&self) -> PathBuf { self.home_dir.clone() } diff --git a/rust/plugin_runtime/src/plugins/applications/windows.rs b/rust/plugin_runtime/src/plugins/applications/windows.rs index a30ecbe..3d10d6a 100644 --- a/rust/plugin_runtime/src/plugins/applications/windows.rs +++ b/rust/plugin_runtime/src/plugins/applications/windows.rs @@ -1,6 +1,4 @@ use std::io::Cursor; -use std::mem; -use std::mem::MaybeUninit; use std::path::PathBuf; use std::ptr; @@ -16,13 +14,13 @@ use windows::Win32::Graphics::Gdi; use windows::Win32::Graphics::Gdi::HDC; use windows::Win32::Storage::FileSystem; use windows::Win32::UI::Controls; -use windows::Win32::UI::Controls::HIMAGELIST; use windows::Win32::UI::Shell; use windows::Win32::UI::WindowsAndMessaging; use windows::core::GUID; use windows::core::HSTRING; use windows::core::PWSTR; +use crate::deno::GauntletJsError; use crate::plugins::applications::DesktopApplication; use crate::plugins::applications::DesktopPathAction; use crate::plugins::applications::resize_icon; @@ -58,16 +56,19 @@ fn windows_application_dirs() -> Vec { #[op2] #[serde] -fn windows_open_application(#[string] file_path: String) -> anyhow::Result<()> { - open::that_detached(file_path)?; +fn windows_open_application(#[string] file_path: String) -> Result<(), GauntletJsError> { + open::that_detached(file_path).map_err(|err| anyhow!(err))?; Ok(()) } #[op2(async)] #[serde] -async fn windows_app_from_path(#[string] file_path: String) -> anyhow::Result> { - spawn_blocking(|| windows_app_from_path_blocking(file_path)).await? +async fn windows_app_from_path(#[string] file_path: String) -> Result, GauntletJsError> { + Ok( + spawn_blocking(|| windows_app_from_path_blocking(file_path)).await + .map_err(|err| anyhow!(err))?? + ) } fn windows_app_from_path_blocking(file_path: String) -> anyhow::Result> { diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index a840afe..766f7ef 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -41,7 +41,6 @@ use gauntlet_common_plugin_runtime::recv_message; use gauntlet_common_plugin_runtime::send_message; use gauntlet_utils::channel::RequestResult; use interprocess::local_socket::ListenerOptions; -use interprocess::local_socket::ToFsName; use interprocess::local_socket::tokio::RecvHalf; use interprocess::local_socket::tokio::SendHalf; use interprocess::local_socket::traits::tokio::Listener; @@ -198,6 +197,7 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run let home_dir = data.dirs.home_dir(); let local_storage_dir = data.dirs.plugin_local_storage(&plugin_uuid); + #[cfg(unix)] let uds_socket_file = data.dirs.plugin_uds_socket(&plugin_uuid); let plugin_cache_dir = data.dirs.plugin_cache(&plugin_uuid)?; let plugin_data_dir = data.dirs.plugin_data(&plugin_uuid)?; @@ -210,14 +210,20 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run // namespaced, removed when both client and server disconnect #[cfg(target_os = "windows")] - let name = name_str - .clone() - .to_ns_name::()?; + let name = { + use interprocess::local_socket::ToNsName; + + name_str + .clone() + .to_ns_name::()? + }; // not namespaced, needs to be cleaned up manually, // by using close-behind semantics and additionally removing it before creating a new runtime #[cfg(unix)] let name = { + use interprocess::local_socket::ToFsName; + let uds_socket_file = uds_socket_file.clone(); // manually remove in case of unexpected situation where removing after connection did not work properly @@ -240,6 +246,7 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run .context("non-uft8 paths are not supported")? .to_string(); + #[cfg(unix)] let uds_socket_file = uds_socket_file .to_str() .context("non-uft8 paths are not supported")? From fee4e9a18b223f5b4eb2d4f50eab72cb77d1cc30 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 31 May 2025 21:35:42 +0200 Subject: [PATCH 16/91] Fix macos build --- rust/management_client/src/views/general.rs | 3 +- .../src/plugins/applications.rs | 49 ++++++++++--- .../src/plugins/applications/macos.rs | 72 ++++++++++--------- .../src/plugins/applications/windows.rs | 9 +-- 4 files changed, 83 insertions(+), 50 deletions(-) diff --git a/rust/management_client/src/views/general.rs b/rust/management_client/src/views/general.rs index 3ec34cd..72171c9 100644 --- a/rust/management_client/src/views/general.rs +++ b/rust/management_client/src/views/general.rs @@ -184,7 +184,8 @@ impl ManagementAppGeneralState { let theme_field = self.theme_field(); - let content = vec![global_shortcut_field, theme_field]; + #[allow(unused_mut)] + let mut content = vec![global_shortcut_field, theme_field]; #[cfg(target_os = "macos")] { diff --git a/rust/plugin_runtime/src/plugins/applications.rs b/rust/plugin_runtime/src/plugins/applications.rs index 1fdd56b..c031efe 100644 --- a/rust/plugin_runtime/src/plugins/applications.rs +++ b/rust/plugin_runtime/src/plugins/applications.rs @@ -23,12 +23,16 @@ mod windows; #[cfg(target_os = "windows")] pub use windows::gauntlet_internal_windows; +#[allow(unused)] +use crate::deno::GauntletJsError; + #[derive(Debug, Serialize)] #[serde(tag = "type")] pub enum DesktopPathAction { #[serde(rename = "add")] Add { id: String, data: DesktopApplication }, #[serde(rename = "remove")] + #[allow(unused)] Remove { id: String }, } @@ -137,8 +141,16 @@ pub fn macos_major_version() -> u8 { pub async fn macos_app_from_path( #[string] path: String, #[string] lang: Option, -) -> anyhow::Result> { - Ok(spawn_blocking(|| macos::macos_app_from_path(&PathBuf::from(path), lang)).await?) +) -> Result, GauntletJsError> { + use std::path::PathBuf; + + use tokio::task::spawn_blocking; + + let result = spawn_blocking(|| macos::macos_app_from_path(&PathBuf::from(path), lang)) + .await + .map_err(|err| anyhow::anyhow!(err))?; + + Ok(result) } #[cfg(target_os = "macos")] @@ -147,8 +159,16 @@ pub async fn macos_app_from_path( pub async fn macos_app_from_arbitrary_path( #[string] path: String, #[string] lang: Option, -) -> anyhow::Result> { - Ok(spawn_blocking(|| macos::macos_app_from_arbitrary_path(PathBuf::from(path), lang)).await?) +) -> Result, GauntletJsError> { + use std::path::PathBuf; + + use tokio::task::spawn_blocking; + + let result = spawn_blocking(|| macos::macos_app_from_arbitrary_path(PathBuf::from(path), lang)) + .await + .map_err(|err| anyhow::anyhow!(err))?; + + Ok(result) } #[cfg(target_os = "macos")] @@ -173,8 +193,12 @@ pub fn macos_application_dirs() -> Vec { #[cfg(target_os = "macos")] #[op2(fast)] -pub fn macos_open_application(#[string] app_path: String) -> anyhow::Result<()> { - std::process::Command::new("open").args([app_path]).spawn_detached()?; +pub fn macos_open_application(#[string] app_path: String) -> Result<(), GauntletJsError> { + use gauntlet_common::detached_process::CommandExt; + std::process::Command::new("open") + .args([app_path]) + .spawn_detached() + .map_err(|err| anyhow::anyhow!(err))?; Ok(()) } @@ -195,20 +219,24 @@ pub fn macos_settings_13_and_post(#[string] lang: Option) -> Vec anyhow::Result<()> { +pub fn macos_open_setting_13_and_post(#[string] preferences_id: String) -> Result<(), GauntletJsError> { + use gauntlet_common::detached_process::CommandExt; std::process::Command::new("open") .args([format!("x-apple.systempreferences:{}", preferences_id)]) - .spawn_detached()?; + .spawn_detached() + .map_err(|err| anyhow::anyhow!(err))?; Ok(()) } #[cfg(target_os = "macos")] #[op2(fast)] -pub fn macos_open_setting_pre_13(#[string] setting_path: String) -> anyhow::Result<()> { +pub fn macos_open_setting_pre_13(#[string] setting_path: String) -> Result<(), GauntletJsError> { + use gauntlet_common::detached_process::CommandExt; std::process::Command::new("open") .args(["-b", "com.apple.systempreferences", &setting_path]) - .spawn_detached()?; + .spawn_detached() + .map_err(|err| anyhow::anyhow!(err))?; Ok(()) } @@ -224,6 +252,7 @@ pub fn macos_get_localized_language() -> Option { .map(|s| s.to_string()) } +#[allow(unused)] pub(in crate::plugins::applications) fn resize_icon(data: Vec) -> anyhow::Result> { let data = image::load_from_memory_with_format(&data, ImageFormat::Png)?; let data = image::imageops::resize(&data, 48, 48, FilterType::Lanczos3); diff --git a/rust/plugin_runtime/src/plugins/applications/macos.rs b/rust/plugin_runtime/src/plugins/applications/macos.rs index 330dddc..a64badc 100644 --- a/rust/plugin_runtime/src/plugins/applications/macos.rs +++ b/rust/plugin_runtime/src/plugins/applications/macos.rs @@ -1,7 +1,5 @@ use std::collections::HashMap; -use std::error::Error; use std::ffi::OsStr; -use std::path::Component; use std::path::Path; use std::path::PathBuf; @@ -10,11 +8,9 @@ use anyhow::anyhow; use cacao::filesystem::FileManager; use cacao::filesystem::SearchPathDirectory; use cacao::filesystem::SearchPathDomainMask; -use cacao::url::Url; use deno_core::ToJsBuffer; use objc2::ClassType; use objc2_app_kit::NSBitmapImageRep; -use objc2_app_kit::NSCalibratedWhiteColorSpace; use objc2_app_kit::NSCompositeCopy; use objc2_app_kit::NSDeviceRGBColorSpace; use objc2_app_kit::NSGraphicsContext; @@ -22,8 +18,6 @@ use objc2_app_kit::NSImage; use objc2_app_kit::NSPNGFileType; use objc2_app_kit::NSWorkspace; use objc2_foundation::CGFloat; -use objc2_foundation::CGPoint; -use objc2_foundation::CGRect; use objc2_foundation::NSDictionary; use objc2_foundation::NSInteger; use objc2_foundation::NSPoint; @@ -31,8 +25,6 @@ use objc2_foundation::NSRect; use objc2_foundation::NSSize; use objc2_foundation::NSString; use objc2_foundation::NSZeroRect; -use plist::Dictionary; -use plist::Value; use regex::Regex; use serde::Deserialize; @@ -335,6 +327,7 @@ fn get_pref_panes_with_kind( }) } +#[allow(unused)] fn get_applications_with_kind( file_manager: &FileManager, directory: SearchPathDirectory, @@ -428,41 +421,43 @@ fn get_items_in_dir(path: PathBuf, extension: &str) -> Vec { // from https://stackoverflow.com/a/38442746 and https://stackoverflow.com/a/29162536 unsafe fn resize_ns_image(source_image: &NSImage, width: NSInteger, height: NSInteger) -> Option> { - let new_size = NSSize::new(width as CGFloat, height as CGFloat); + unsafe { + let new_size = NSSize::new(width as CGFloat, height as CGFloat); - let bitmap_image_rep = NSBitmapImageRep::initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel( - NSBitmapImageRep::alloc(), - std::ptr::null_mut::<*mut _>(), - width, - height, - 8, - 4, - true, - false, - NSDeviceRGBColorSpace, - 0, - 0, - )?; + let bitmap_image_rep = NSBitmapImageRep::initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel( + NSBitmapImageRep::alloc(), + std::ptr::null_mut::<*mut _>(), + width, + height, + 8, + 4, + true, + false, + NSDeviceRGBColorSpace, + 0, + 0, + )?; - bitmap_image_rep.setSize(new_size); + bitmap_image_rep.setSize(new_size); - NSGraphicsContext::saveGraphicsState_class(); + NSGraphicsContext::saveGraphicsState_class(); - let context = NSGraphicsContext::graphicsContextWithBitmapImageRep(&bitmap_image_rep) - .expect("should be present because just saved"); + let context = NSGraphicsContext::graphicsContextWithBitmapImageRep(&bitmap_image_rep) + .expect("should be present because just saved"); - NSGraphicsContext::setCurrentContext(Some(&context)); + NSGraphicsContext::setCurrentContext(Some(&context)); - let rect = NSRect::new(NSPoint::new(0.0, 0.0), new_size); - source_image.drawInRect_fromRect_operation_fraction(rect, NSZeroRect, NSCompositeCopy, 1.0); + let rect = NSRect::new(NSPoint::new(0.0, 0.0), new_size); + source_image.drawInRect_fromRect_operation_fraction(rect, NSZeroRect, NSCompositeCopy, 1.0); - NSGraphicsContext::restoreGraphicsState_class(); + NSGraphicsContext::restoreGraphicsState_class(); - // TODO i guess this doesn't work for 2x image + // TODO i guess this doesn't work for 2x image - let data = bitmap_image_rep.representationUsingType_properties(NSPNGFileType, &NSDictionary::dictionary())?; + let data = bitmap_image_rep.representationUsingType_properties(NSPNGFileType, &NSDictionary::dictionary())?; - Some(data.bytes().to_vec()) + Some(data.bytes().to_vec()) + } } fn get_application_icon(app_path: &Path) -> anyhow::Result { @@ -495,8 +490,10 @@ struct Info { // macOS only icon fields #[serde(rename = "CFBundleIconFile")] + #[allow(unused)] bundle_icon_file: Option, #[serde(rename = "CFBundleIconName")] + #[allow(unused)] bundle_icon_name: Option, } @@ -523,6 +520,11 @@ struct SystemVersion { #[derive(Deserialize)] #[serde(untagged)] enum SidebarSection { - Content { content: Vec }, - Title { title: String }, + Content { + content: Vec, + }, + Title { + #[allow(unused)] + title: String, + }, } diff --git a/rust/plugin_runtime/src/plugins/applications/windows.rs b/rust/plugin_runtime/src/plugins/applications/windows.rs index 3d10d6a..ce4eaed 100644 --- a/rust/plugin_runtime/src/plugins/applications/windows.rs +++ b/rust/plugin_runtime/src/plugins/applications/windows.rs @@ -65,10 +65,11 @@ fn windows_open_application(#[string] file_path: String) -> Result<(), GauntletJ #[op2(async)] #[serde] async fn windows_app_from_path(#[string] file_path: String) -> Result, GauntletJsError> { - Ok( - spawn_blocking(|| windows_app_from_path_blocking(file_path)).await - .map_err(|err| anyhow!(err))?? - ) + let result = spawn_blocking(|| windows_app_from_path_blocking(file_path)) + .await + .map_err(|err| anyhow!(err))??; + + Ok(result) } fn windows_app_from_path_blocking(file_path: String) -> anyhow::Result> { From 41107d2043561385aa5161358719b736e83e8d63 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 1 Jun 2025 13:10:43 +0200 Subject: [PATCH 17/91] Update README.md --- README.md | 453 +++++------------------------- docs/THEME.md | 21 -- docs/architecture-blocks.plantuml | 17 -- docs/architecture-blocks.png | Bin 10352 -> 0 bytes docs/architecture.plantuml | 31 -- docs/architecture.png | Bin 31378 -> 0 bytes 6 files changed, 76 insertions(+), 446 deletions(-) delete mode 100644 docs/THEME.md delete mode 100644 docs/architecture-blocks.plantuml delete mode 100644 docs/architecture-blocks.png delete mode 100644 docs/architecture.plantuml delete mode 100644 docs/architecture.png diff --git a/README.md b/README.md index e0b7428..158272c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Web-first cross-platform application launcher with React-based plugins > [!WARNING] > Launcher is being developed by single developer in their free time. -> Changes may be few and far between. +> Changes may be slow but steady > > There will probably be breaking changes which will be documented in [changelog](CHANGELOG.md). @@ -16,395 +16,92 @@ Web-first cross-platform application launcher with React-based plugins ## Demo +Slightly outdated demo + https://github.com/user-attachments/assets/19964ed6-9cd9-48d4-9835-6be04de14b66 ## Features - Plugin-first - - Plugins are written in TypeScript - - Plugins can have the following functionality - - Create UI - - One-shot commands - - Dynamically provide list of one-shot commands - - Render quick "inline" content directly under main search bar based on value in it - - Get content from and add to Clipboard - - Plugins are distributed as separate branch in Git repository, meaning plugin distribution doesn't need any central - server - - Plugins IDs are just Git Repository URLs -- Built-in functionality is provided by bundled plugin + - Plugins are written in TypeScript + - Extensive plugin API + - Create UI views + - One-shot commands + - Dynamically provide list of one-shot commands + - Render quick "inline" content directly under main search bar based on value in it + - Get content from and add to Clipboard + - Plugins are distributed as separate branch in Git repository, meaning plugin distribution doesn't need any central + server + - Plugins IDs are just Git Repository URLs + - [React](https://github.com/facebook/react)-based UI for plugins + - Implemented using custom React Reconciler (no Electron) + - [Deno JavaScript Runtime](https://github.com/denoland/deno) + - Deno allows to sandbox JavaScript plugin code for better security + - Plugins are required to explicitly specify what permissions they need to work + - Node.js is used to run plugin tooling, but as a plugin developer you will always write code that runs on Deno +- Designed with cross-platform in mind from the beginning +- Commands and Views can be run/opened using custom global shortcuts +- Custom search alias can be assigned to Commands or Views +- Custom theme support +- Built-in functionality is provided by bundled plugins - Applications: shows applications installed on the system in search results + - Plugin also tracks windows and which application they belong to, so opening already opened application will by default bring up previously created window + - Not all systems are supported at the moment. See [feature support](https://gauntlet.sh/docs/feature-support) - Calculator: shows result of mathematical operations directly under main search bar - Includes converting currency using exchange rates - Powered by [Numbat](https://github.com/sharkdp/numbat) - - Settings: open Gauntlet Settings - - More to come, see [#15](https://github.com/project-gauntlet/gauntlet/issues/15) -- [React](https://github.com/facebook/react)-based UI for plugins - - Implemented using custom React Reconciler (no Electron) - - [iced-rs](https://github.com/iced-rs/iced) is used for UI -- [Deno JavaScript Runtime](https://github.com/denoland/deno) - - Deno allows us to sandbox JavaScript code for better security - - Plugins are required to explicitly specify what permissions they need to work - - NodeJS is used to run plugin tooling, but as a plugin developer you will always write code that runs on Deno - Frecency-based search result ordering - Frecency is a combination of frequency and recency - More often the item is used the higher in the result list it will be, but items used a lot in the past will be ranked lower than items used the same amount of times recently - - Currently, there is no fuzzy matching. Results are matched per word by substring -- Designed with cross-platform in mind - - Permissions - - By default, plugins do not have access to host system - - If plugin asked for access to filesystem, env variables or running commands, it is required to specify - which operating systems it supports. - - If plugin doesn't use filesystem, env variables or running commands and just uses network and/or UI, it - is cross-platform - - Shortcuts - - Plugins are allowed to use only limited set of keys for shortcuts to support widest possible range of keyboards - - Only upper and lower-case letters, symbols and numbers - - Shortcut can have either `"main"` or `"alternative"` kind so plugins do not need to specify shortcut separately for each OS - - `"main"` shortcut requires following modifiers - - Windows and Linux: CTRL - - macOS: CMD - - `"alternative"` shortcut requires following modifiers - - Windows and Linux: ALT - - macOS: OPT - - Whether SHIFT is also required depends on character specified for shortcut, e.g `$` will - require SHIFT to be pressed, while `4` will not + - Results are matched per word by substring ##### OS Support ##### Official - Linux X11 - - Application plugin depends on `gtk-launch` - macOS M1 ##### Best-effort - Linux Wayland - - LayerShell support required - - Application plugin depends on `gtk-launch` + - Gnome Wayland is not yet supported, see [#40](https://github.com/project-gauntlet/gauntlet/issues/40) - Windows - macOS Intel -##### Planned features - -- See [#13](https://github.com/project-gauntlet/gauntlet/issues/13) -- See [#15](https://github.com/project-gauntlet/gauntlet/issues/15) -- See [#16](https://github.com/project-gauntlet/gauntlet/issues/16) - -##### Plugin APIs - -- UI - - Detail - - Form - - Action Panel - - List - - Grid - - Inline - - View directly under main search bar - - Requires separate permission to be explicitly specified in manifest because it reads everything user enters in main search bar -- Stack-based Navigation -- Assets - - Files placed into `assets` directory in root of plugin repository are accessible at plugin runtime using `assetData` function -- Preferences - - Preferences defined in plugin manifest can be set by user and are accessible at plugin runtime using `pluginPreferences` and `entrypointPreferences` functions -- Clipboard - - Accessible via `Clipboard` api - - Requires separate permission to be explicitly specified in manifest -- HUD - - Shows small popup window with feedback information - - Accessible via `showHud` function -- React Helper Hooks - - `usePromise` - - Helper to run promises in a context of React view - - Returns `AsyncState` object which contains `isLoading`, `error` and `data` properties - - `useStorage` - - Helper to store data between entrypoint, plugin and application runs - - Follows API similar to `useState` built-in React Hook - - Uses `localStorage` internally - - `useCache` - - Helper to store data between entrypoint runs but will be reset when plugin or application is restarted - - Follows API similar to `useState` built-in React Hook - - Uses `sessionStorage` internally - - `useCachedPromise` - - Helper to run promises with caching done automatically - - Follows `stale-while-revalidate` caching strategy - - Uses `usePromise` and `useCache` Hooks internally - - `useFetch` - - Helper to run `fetch()` with caching done automatically - - Follows `stale-while-revalidate` caching strategy - - Uses `useCachedPromise` Hook internally - ## Getting Started -### Create your own plugin +### Install Gauntlet -- Go to [plugin-template](https://github.com/project-gauntlet/plugin-template) and create your own GitHub repo from it. -- Run `npm run dev` to start dev server (requires running application server) - - Dev server will automatically refresh the plugin on any file change -- Do the changes you need - - You can configure plugin using [Plugin manifest](#plugin-manifest) - - Documentation is, at the moment, basically non-existent but TypeScript declarations in `@project-gauntlet/api` - and `@project-gauntlet/deno` should help - - For examples see [Dev Plugin](dev_plugin). It is very busy because it is used for Gauntlet development, but it has examples of pretty much every available API -- Push changes to GitHub -- Run `publish` GitHub Actions workflow to publish plugin to `gauntlet/release` branch -- Profit! +See [Installation](https://gauntlet.sh/docs/installation) + +### Global Shortcut + +Main window can be opened using global shortcut or CLI command: +- Global Shortcut (can be changed in Settings) + - Windows: ALT + Space + - Linux X11: Super + Space + - Linux Wayland + - Global shortcut may not be supported, see [feature support](https://gauntlet.sh/docs/feature-support) + - Please use CLI command instead, and invoke it using window manager specific approach + - macOS: CMD + Space +- CLI command + - `gauntlet open` ### Install plugin -Plugins are installed in Settings UI. Use Git repository url of the plugin to install it. +Plugins are installed in Settings UI. Use Git repository url of the plugin to install it, e.g. `https://github.com/project-gauntlet/readme-demo-plugin.git` + +### Create your own plugin + +See [Getting started with plugin development](https://gauntlet.sh/docs/plugin-development/getting-started) ![](docs/settings_ui.png) -### Install application - -#### macOS - -Although it is possible to install Gauntlet by using `.dmg` directly, application doesn't have auto-update functionality so it is recommended to install using `brew` package manager. - -Brew package: [link](https://formulae.brew.sh/cask/gauntlet) - -To install run: -``` -brew install --cask gauntlet -``` - -To start, manually open application. - -#### Windows - -Download `.msi` at [Releases page](https://github.com/project-gauntlet/gauntlet/releases/latest) and open to install Gauntlet - -Note: application doesn't have auto-update functionality, and has to be updated manually - -To start, manually open application. - -#### Arch Linux - -AUR package: [link](https://aur.archlinux.org/packages/gauntlet-bin) - -To install run: -``` -yay -S gauntlet-bin -``` - -To start `systemd` service run: -``` -systemctl --user enable --now gauntlet.service -``` - -#### Nix - -The nix flake in this repository is community maintained. If you face a problem, please create an issue and hopefully somebody will work on it. - -To install, you either know what to do, or you can read more [here](nix/README.md). - -#### Other Linux Distributions - -At the moment application is only available for Arch Linux and Nix. If you want to create a package for other distributions see [Application packaging for Linux](#application-packaging-for-Linux) - -### Global Shortcut -Main window can be opened using global shortcut or CLI command: -- Shortcut: - - Windows: ALT + Space - - Linux X11: Super + Space - - Linux Wayland: No global shortcut. Please use CLI command - - macOS: CMD + Space - - Can be changed in Settings -- CLI command: - - `gauntlet open` - -## Configuration - -### Plugin manifest - -```toml -[gauntlet] -name = 'Plugin Name' -description = """ -Plugin description -""" - -[[preferences]] # plugin preference -name = 'testBool' -type = 'enum' # available values: 'number', 'string,' 'bool', 'enum', 'list_of_strings', 'list_of_numbers', 'list_of_enums' -default = 'item' # type of default depends on type field. Currently, list types have no default -description = "Some preference description" -enum_values = [{ label = 'Item', value = 'item'}] # defines list of available enum values, required for types "enum" and "list_of_enums" - -[[entrypoint]] -id = 'ui-view' # id for entrypoint -name = 'UI view' # name of entrypoint -path = 'src/ui-view.tsx' # path to file, default export is expected to be function React Function Component -icon = 'icon.png' # optional, path to file inside assets dir -type = 'view' -description = 'Some entrypoint description' - -[[entrypoint.preferences]] # entrypoint preference -name = 'boolPreference' -type = 'bool' -default = true -description = "bool preference description" - -[[entrypoint.actions]] -id = 'someAction' # id of action, needs to align with value in "id" property -description = "demo action description" -shortcut = { key = ':', kind = 'main'} # key string only accepts lower and upper-case letters, numbers and symbols. kind can be "main" or "alternative" - -[[entrypoint]] -id = 'command-a' -name = 'Command A' -path = 'src/command-a.ts' # path to file, the whole file is a js script -type = 'command' -description = 'Some entrypoint description' - -[[entrypoint]] -id = 'entrypoint-generator' -name = 'Entrypoint generator' -path = 'src/entrypoint-generator.ts' -type = 'entrypoint-generator' -description = 'Some entrypoint description' - -[[entrypoint]] -id = 'inline-view' -name = 'Inline view' -path = 'src/inline-view.tsx' -type = 'inline-view' -description = 'Some entrypoint description' - -[permissions] -network = ["github.com", "example.com:8833"] -clipboard = ["read", "write", "clear"] -main_search_bar = ["read"] - -# if specified requires supported_system to be specified as well -environment = ["ENV_VAR_NAME"] - -# if specified requires supported_system to be specified as well -system = ["apiName"] - -# if specified requires supported_system to be specified as well -[permissions.filesystem] -read = [ - "C:\\ProgramFiles\\test", - "C:/ProgramFiles/test", - "{windows:user-home}\\test", - "{windows:user-home}/test", - "{linux:user-home}/test", - "/etc/test" -] -write = ["/home/exidex/.test"] - -# if specified requires supported_system to be specified as well -[permissions.exec] -command = ["ls"] -executable = ["/usr/bin/ls"] - -[[supported_system]] -os = 'linux' # 'linux', 'windows' or 'macos' - -``` - -### Application config - -Located at `$XDG_CONFIG_HOME/gauntlet/config.toml` for Linux. Not used at the moment. - -## CLI - -### Application - -The Application has a simple command line interface - -- `gauntlet` - starts server - - `gauntlet --minimized` - starts server without opening main window -- `gauntlet open` - opens application window, can be used instead of global shortcut -- `gauntlet settings` - settings, plugin installation and removal, preferences, etc - -### Dev Tools - -[`@project-gauntlet/tools`](https://www.npmjs.com/package/@project-gauntlet/tools) contains separate CLI tool for plugin -development purposes. It has following commands: - -- `gauntlet dev` - - Starts development server which will automatically refreshed plugin on any file change. -- `gauntlet build` - - Builds plugin -- `gauntlet publish` - - Publishes plugin to separate git branch. Includes `build` - - `publish` assumes some things about git repository, so it is recommended to publish plugin from GitHub Actions - workflow - -[Plugin template](https://github.com/project-gauntlet/plugin-template) has nice `npm run` wrappers for them. - ## Theming -See [THEME.md](./docs/THEME.md) - -## Architecture - -The Application consists of 4 parts: server, frontend, plugin runtime and settings. -Each plugin runs in separate plugin runtime in separate OS process. Each plugin is its own sandboxed Deno Worker. -In plugin manifest it is possible to configure permissions which will allow plugin to have access to filesystem, -network, environment variables or subprocess execution. -Server saves plugins themselves and state of plugins into SQLite database. - -Frontend is GUI module that uses [iced-rs](https://github.com/iced-rs/iced) as a GUI framework. It is run in the same process as a server. - -Plugins can create UI using [React](https://github.com/facebook/react). -Plugin Runtime implements custom React Reconciler (similar to React Native) which renders GUI components to frontend. -Plugin Runtime listens on signals from frontend, so when user opens view defined by plugin, frontend sends an open-view request. -Plugin Runtime then receives it, runs React render and React Reconciler makes requests to the frontend containing information what actually should be rendered. -When a user interacts with the UI by clicking button or entering text into form, -frontend sends events to server to see whether any re-renders are needed. - -Settings is a GUI application runs in separate process that communicates with server using a simple request-response approach. - -Simplified communication: -![](docs/architecture.png) - -Components: -![](docs/architecture-blocks.png) - -Plugins (or rather its compiled state: manifest, js code and assets) are distributed via Git repository in `gauntlet/release` branch (similar to GitHub Pages). -Which means there is no one central place required for plugin distribution. -And to install plugin all you need is Git repository url. - -## Application packaging for Linux - -This section contains a list of things -that could be useful for someone who wants to package application for Linux distribution. -If something is missing, please [create an issue](https://github.com/project-gauntlet/gauntlet/issues). - -Application is already packaged for [Arch Linux](#arch-linux) and [Nix](#nix) so you can use them as examples. - -Relevant CLI commands: - -- `$ gauntlet --minimized` - - Server needs to be started when user logs in, e.g. using `systemd` service -- `$ gauntlet open` - - Main windows is usually opened using [global shortcut](#global-shortcut), this CLI command can be used in cases where global shortcut functionality is not available -- `$ gauntlet settings` - - Settings are usually started on demand from Gauntlet itself - -`.desktop` sample file can be found [here](assets/linux/gauntlet.desktop) - -`systemd` service sample file can be found [here](assets/linux/gauntlet.service) - -###### Directories used - -- data dir - `$XDG_DATA_HOME/gauntlet` or `$HOME/.local/share/gauntlet` - - contains application state `data.db` -- cache dir - `$XDG_CACHE_HOME/gauntlet` or `$HOME/.cache/gauntlet` - - contains icon cache -- config dir - `$XDG_CONFIG_HOME/gauntlet` or `$HOME/.config/gauntlet` - - contains application config `config.toml` - - application will never do changes to config file -- state dir - `$XDG_STATE_HOME/gauntlet` or `$HOME/.local/state/gauntlet` - - contains log files created by plugin development -- `.desktop` files at locations defined by [Desktop Entry Specification](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html) - -Client and Setting applications have GUI and therefore use all the usual graphics-related stuff from X11. -Wayland support requires LayerShell protocol `zwlr_layer_shell_v1`. +See [Theming](https://gauntlet.sh/docs/theming) ## Building Gauntlet + You will need: - NodeJS - Rust @@ -412,6 +109,8 @@ You will need: - CMake (not used by the project itself, but is required by a dependency) - On Linux: `libxkbcommon-dev` (note: name may differ depending on used distribution) +### Dev + To build dev run: ```bash npm ci @@ -419,14 +118,35 @@ npm run build npm run build-dev-plugin cargo build ``` -In dev (without "release" feature) application will use only directories inside project directory to store state or cache. +In dev (without "release" feature) application will use directories ONLY inside project directory to store state or cache, to avoid messing up global installation -To build release run: +### Not-yet-packaged + +To build not-yet-packaged release binary, run: ```bash npm ci npm run build cargo build --release --features release ``` + +### Packaged +To build os-specific package, run one of the following: + +macOS: +```bash +npm run build-macos-project --workspace @project-gauntlet/build +``` + +Windows: +```bash +npm run build-windows-project --workspace @project-gauntlet/build +``` + +Linux: +```bash +npm run build-linux-project --workspace @project-gauntlet/build +``` + But the new version release needs to be done via GitHub Actions ## Contributing @@ -435,29 +155,8 @@ If you'd like to help build Gauntlet you can do it in more ways than just contri - Reporting a bug or UI/UX problem - Creating a plugin -If you are looking for things to do see pinned [issues](https://github.com/project-gauntlet/gauntlet/issues). - -For simple problems feel free to open an issue or PR and tackle it yourself! - +For simple problems feel free to open an issue or PR and tackle it yourself. For more significant changes please contact creators on Discord (invite link on top of README) and discuss first. All and any contributions are welcome. -## Versioning - -### Application - -Application uses simple incremental integers starting from `1`. -It doesn't follow the SemVer versioning. -Given application's reliance on plugins, once it is stable, -introducing breaking changes will be done carefully (if at all) and will be given a reasonable grace period to migrate. -SemVer is about a hard cutoff between major versions with breaking changes, which doesn't fit this kind of application. -Before application is declared stable, breaking changes could be done without a grace period. - -### Tools - -[`@project-gauntlet/tools`](https://www.npmjs.com/package/@project-gauntlet/tools) uses SemVer. - -### Plugins - -Plugins only have the latest published "version". diff --git a/docs/THEME.md b/docs/THEME.md deleted file mode 100644 index 98af1b0..0000000 --- a/docs/THEME.md +++ /dev/null @@ -1,21 +0,0 @@ -# Gauntlet Theming - -Currently, in Gauntlet with themes it is possible to change (list is likely be extended with future updates): -- Colors of text and background -- Window border color, width and radius -- Border radius of components in content - -Theming is only affects main window and doesn't affect settings - -Theme config file is in TOML format - -Theme config file locations: -- Windows: `C:\Users\Username\AppData\Roaming\Gauntlet\config\theme.toml` -- Linux: `$XDG_CONFIG_HOME/gauntlet/theme.toml` -- macOS: `$HOME/Library/Application Support/dev.project-gauntlet.gauntlet/theme.toml` - -Currently, theme change is only applied after application restart - -Any errors in theme parsing will be shown in application logs - -See bundled themes for examples [here](./../bundled_themes) diff --git a/docs/architecture-blocks.plantuml b/docs/architecture-blocks.plantuml deleted file mode 100644 index fa3010d..0000000 --- a/docs/architecture-blocks.plantuml +++ /dev/null @@ -1,17 +0,0 @@ -@startuml - -node "Frontend" -node "Server" -node "Deno worker 1" as Deno1 -node "Deno worker 2" as Deno2 -node "Deno worker 3" as Deno3 -node "Deno worker 4" as Deno4 - -[Frontend] --> [Server] - -[Server] --> [Deno1] -[Server] --> [Deno2] -[Server] --> [Deno3] -[Server] --> [Deno4] - -@enduml \ No newline at end of file diff --git a/docs/architecture-blocks.png b/docs/architecture-blocks.png deleted file mode 100644 index 7d72943b9c537837450c2359ded3e99016224669..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10352 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

BAmNRdvDaNqt`?0{L0O zd?a#17CVMZ9FhhKtq@{v6@9C{J1}m9_~S1h$FJX@83i{J(%db1UAW(kbDbtNiCcP))$8SwzQlrEK4u`-Hsw4jcW}4@nKAIB5`#{|O&RCpdo9wyL zI2c90>mbLFLF`dV+1+}YxV5qtx?2qy72vxQtoS}VP+y<$+3$hO#G_Y%;uQTJjtq5X zv+PwSeju0 zBZtbj7qr88JSi>W?X=+A4$l#EJO5ZBve`*jk)}6IJ%0f>Z_wgK{v@gJUkN3MFRvyC zp^$SScybSaQ;>a#nl86(xKN`xP1X^j3OQuS$UQ{T_C2=iPTH9~Z>$%VzI$#Ts($ku zT0s}@IXi*uUF>f-01lW%f#`4ee~R#b2RX!f%eXBk&(6+1E4gcLTO1>#HLylSL}j+y zRMcZ6wf9`@05stZ;BT!YH}|QR8@iuv7Xa9!(|Rz*2qYTaM@k*w!p%7F}dE? zE3CS8ZGafvT&kF;Y+b6DTRM1txy}O+7PO&qE>_BJ1!!ZuLLJgCGzuW&S7gIZ3gQ4V z=32s?vaPz+{tbz9um`}(4DR&)f8H|}s3~nX_-~*LsyY=q;wvgDrf$bc{TqHi?4_dQ zAV! Server: list of views and commands request -Server --> Frontend: list of views and commands response - -== Frontend: Initial View Render == - -Frontend -> Server: open-view event -Server -> Frontend: render components request -Frontend --> Server: render components response - -== Frontend: Command Execution == - -Frontend -> Server: execute command - -== Frontend: View Update On Event == - -Frontend -> Server: button click, key press in input component, etc -Server -> Frontend: render components request -Frontend --> Server: render components response - -== Settings == - -Settings -> Server: request -Server --> Settings: response - -@enduml \ No newline at end of file diff --git a/docs/architecture.png b/docs/architecture.png deleted file mode 100644 index bb065023fc094b83f889fe97df6f5e617aa2332e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31378 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%0D! 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 Date: Sun, 1 Jun 2025 13:24:47 +0200 Subject: [PATCH 18/91] Add missing focus screenshot scenarios for documentation --- example_plugins/scenarios/ui_grid/focus/default.json | 3 +++ example_plugins/scenarios/ui_list/focus/default.json | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 example_plugins/scenarios/ui_grid/focus/default.json create mode 100644 example_plugins/scenarios/ui_list/focus/default.json diff --git a/example_plugins/scenarios/ui_grid/focus/default.json b/example_plugins/scenarios/ui_grid/focus/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_grid/focus/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_list/focus/default.json b/example_plugins/scenarios/ui_list/focus/default.json new file mode 100644 index 0000000..904b00b --- /dev/null +++ b/example_plugins/scenarios/ui_list/focus/default.json @@ -0,0 +1,3 @@ +{ + "type": "RequestViewRender" +} \ No newline at end of file From 547f50203083487fde8e55736ccd08eb18bc353f Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 2 Jun 2025 20:01:36 +0200 Subject: [PATCH 19/91] Fix deadlock during database call while typing in main search bar --- rust/server/src/plugins/data_db_repository.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/rust/server/src/plugins/data_db_repository.rs b/rust/server/src/plugins/data_db_repository.rs index 23c7494..2aee1f7 100644 --- a/rust/server/src/plugins/data_db_repository.rs +++ b/rust/server/src/plugins/data_db_repository.rs @@ -588,12 +588,22 @@ impl DataDbRepository { &self, plugin_id: &str, entrypoint_id: &str, + ) -> anyhow::Result> { + let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; + self.action_shortcuts_with_executor(plugin_id, entrypoint_id, &connection) + } + + fn action_shortcuts_with_executor( + &self, + plugin_id: &str, + entrypoint_id: &str, + connection: &Connection, ) -> anyhow::Result> { let DbReadPluginEntrypoint { actions, actions_user_data, .. - } = self.get_entrypoint_by_id(plugin_id, entrypoint_id)?; + } = self.get_entrypoint_by_id_with_executor(plugin_id, entrypoint_id, connection)?; let actions_user_data: HashMap<_, _> = actions_user_data .into_iter() @@ -856,7 +866,7 @@ impl DataDbRepository { .collect::, _>>()? .into_iter() .map(|row| { - let shortcuts = self.action_shortcuts(&row.plugin_id, &row.id)?; + let shortcuts = self.action_shortcuts_with_executor(&row.plugin_id, &row.id, &connection)?; Ok::<_, anyhow::Error>((row.plugin_id, shortcuts)) }) From 04e0dfd44a57eb246a866e94050d783ad6023089 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 2 Jun 2025 20:13:39 +0200 Subject: [PATCH 20/91] Update settings ui screenshot in README.md --- README.md | 6 +++--- docs/settings_ui.png | Bin 532057 -> 694744 bytes 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 158272c..f0bbc77 100644 --- a/README.md +++ b/README.md @@ -88,14 +88,14 @@ Main window can be opened using global shortcut or CLI command: ### Install plugin -Plugins are installed in Settings UI. Use Git repository url of the plugin to install it, e.g. `https://github.com/project-gauntlet/readme-demo-plugin.git` +Plugins are installed in Settings UI. Use Git repository url of the plugin to install it, e.g. `https://github.com/project-gauntlet/readme-demo-plugin.git` + +![](docs/settings_ui.png) ### Create your own plugin See [Getting started with plugin development](https://gauntlet.sh/docs/plugin-development/getting-started) -![](docs/settings_ui.png) - ## Theming See [Theming](https://gauntlet.sh/docs/theming) diff --git a/docs/settings_ui.png b/docs/settings_ui.png index c7882d915278c32096d335729dc8deb600863b17..8df3f45d7df00d4ed4bf822d66592e54dfbe938f 100644 GIT binary patch literal 694744 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%0D+QS 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�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%0AxwfWQeEHBE^<2}!L`G$Y2A6@p9#)t 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 literal 532057 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 z z)~|IS-A0@e+Q~Z7>jS-&rH&=j(bEGAswnZbde_N|Co3bYO>CSB06AjSJNT_u)n+Jm zZ`*ZC4KM1KYyYDw;LkkHpolfU{d(V?-lQyI_lm~2p{%@&mQ}8k-1~rOZ!MiP_~MuL z*(XEZGfC4)%f6H+N6J&mJx0D9VGwjLzxUhQ9#s%&ExCDiniti2Y}vQB%%`-rYBgdl z7_nPY)b@|UsJ8vGcX2=|!mW03ntOB`Q?eFR*0ioF`&T6&J z61TIr>eas6JhcFDsW@wgy4a47BsXYz87`4Z_6&=&4DnFPc6ywfLi?fdOv!s05)wJE zuv>| zo3s}54i`xuWp=b3-!oQF_E1-k0xH@Vm2uH9YCUJGNV_O$LJV;|)WqG8l&+Wm6Fr zNqo#15;zJ{yfP%GZ}_;|j%e$Zw2IOxefU$1ftr<^2ht>yEVjWbXBe5LmVGz7Rc$c8 zeA7Ma_)}RUd7ReFf@`BlRk%+bgM##>VBOoewF&WNyXs_UUut^U zh|e_9(&dC3$?6x_pd)!eYBg&u&Ini9Qn8+Ef3LZA>Gl;sji;%g-*g1@I)VxlGX9dl z{if>(eiQzn+7AH!$W0=XDe@zhVPX92@&+b*|emb%?gYF1$pf2D>gT2+}wOh_G9Vo)H(JOY=!jYqv4%fD@Jn!O2|^&}#7(|WflwXJ>~Q2Tv7zWX z((tW|wcV=mK>X|~8m(yf7M}%kdy#ikGuZ>@-tWiKSf0L(92P3sK)crg7kJAyPD2u6 zrrroM^40VH(rj&F^48@FbMRs{*5;mGeqY$FV$#9Q{DG{cu&!SQ&{-$IY%|5q zb8Bj`aB(+!_Chp(dJ!ic~2_H%Z1&s1y|g` zGWMf~fmTE*gC9{r9R3zCxEnsds5YF>;Lds!uQuYPuQ3^}((FC6hkUG*G0ug@IriTK zG35IXnFFd?f;dtf?!-(og+k1btJ-n3FOzlZ`ck-!myH1f`e_L%zMszlS`T)e^BYA~ zNrT&`4|=Cvh#Ok`(wlao)ce==BigH>mf%mX7hOm``3_^*anRK23YMHO8aT_!r2t2Axf%=>UG;%jWm$4>q@|jaq@3 z!t%8Sn&cqe#~eX#xC&yPv;{54z@$m3TR1m@$&n*wO46D4-`r^Q2D~JhmQ|A83m#pb(E1# zygLKl*CVZmre;;-)Q@R2HaN7-zhpX<%8q6$V&nzg|-7Vfi z7O^YRYgT4d4Ek1jNtM|cKG!Y%?%Oe41u{NsqK1-_>G&jC-I&@t9T1igQR^z_KM<)c zi>O}qXzFBM!0V+d9`C13orW=8*lkRA*fVfu>>r252-eE#R2rFAz|cd0v&Ft$8_~Tu zR{*Gw`oU;s8MT*RJAa3|t|DAA*pAz+&OG<%Xeh`mM%UJ^d&=Xz7m#IyN3am1A6A26 z^!c?NU$TcR7W&zFf4QXPzJ#N7r8MOr)C*Y01 z@)_0abZw`MwU-r}bH4J)Drvr?JPx@7+l|34*48VpM4rDhPLsc>zY6~IJaKZ;@-uGv zM3wX;gJe`o@^zwDHEU3T&>o^e+;|sTb0*0TaP0h)vNKg#OMAbwq%`_d6@0pP*fqsZ z=PE=`(hM-w6s`frPotQTv-&N~p2M%%x7O^MEO8J8$N>5LPVDI~J zChxDjZ5#0MGmu`AlZgEEon=o3RF6T-tgR_cM?liGs12vAa{_i)AL3B`WUcs(h`ToU zYyyG4n-rLO&%M9-Q{thJU6M)g$S#usk#l3vCw-X1gIusmI@+eQkF2ywvB_5RT}0ZR zHq6-d$&`q2gDxE`+vvI0)A;E0d3`o!aY2~DDDF47-KkI+V(^9>ku=ef<(H92E+PkMw~!K?EUdRndl_6c zF;z)!9vV|&Hj+e_@}GZWkBU+7yWKeh zxB}&?cGj#Xxp|MDw7d|f{JPSquIkRlq)Uv|RX+6-HpIHhPDuDuAt~pZrnQIS?!T?1CG#Yz~i8 z8NMSnhC@$RtqJV9$(O$M-F*=#@@a2}_U0KQKE=&wG-MQw4ETA3?eQGDtG@LjuqiQY z=Kb6VA9;jQUUtC}vv{c?tuV8K6u*`gA6V4CLSZUNt^72-;v!zyS2T7%k}C68y6|T_ zdQ>z3L{-!cG1KVO=Afhvj^U~sK{CNWR|#9f@x;yy_zcI?ZuN> z3!5J!7-{9mdl3yI*5<>V;e7yvASiSG`;g3%B@pBzA2*)RKv|0p_-n)4N`hxT9?s;-6PKl(Pf& zA>zeL)qYD{YX;#$Qs3%Cp4uKY5%t`u(C=zi+@xAsl1U_dZm{AcpvnUKe>LHn$xprA zMed1S6QR@jqdS@8Ns8B!+g=`gM-V?tJZb7nUa4({?rzOkCvU1#JwRZ!MEx|OzvEwK4h)T+*7ZGal*F1*qH7q$P_PG@A(sC(=Is=n6mWG>Nd)pXZ zX!-OMUG(AY@k6q^c^yq^^-_x39h_s_xf+Q*{I(Vs8a9khKZ5t z&y`BSK~Ih6Aa^e9d}%M$UZeij59l_icv-~jUbgjbg_4cWMLSnM?|jsVMC?8s(b#$@ zDDGR!6Cuwb;d@r0AA&0T2=S;1= z?WuhYI-Oyy|CCtoN^lNYvI6?!#U$?n$DqiSwY;#o|!FkcD$N1sD_>e;p} z=!?VmdWen zl;==!jIY}0YDv4i5st9nygNe^PRQb~tS6`&#Jg;5pAO`AdD`zs@hl)IN3#PaVOrE0 zsi^$*WYgfq%AMB=8;3jx4kzf`oLr-+RpxA0+`dc&>=V7);#U!SPmO5aKx7~iU0X(t z#`~Q1Zk@O8{jdRIgd$kkiVfs-`ONV0LIrbBQC_0wE<)39yFtryG{dpD(;#Wicze;5 zWL>&a^$bl{;ScyL#pL|s=g`x(_W-?c&z>JWGEjuhHDl^nh4ube%Q52P)8oF;dVJJo zJ;BtVtDI#9T=f-IPQWC6k$Inayj}#2eo7=`|l>tqc~k@vfIxWPQ!EWH`o$Y!(RemjJIiO~Ms! ziPd$9Evn4IlrBEsyT>y4v3LlU^+Nz6qyBwTk;kHcU3*$!@3Q=!sijka>!%|kG3~^nbgAX{E_Ja= zNQaUyZxWiMZMHr>EWb6AaV#%|+OkxhMyGYVc6T=1ciW3qb@Op5^)l^Ezq?uwKI`j% zyQoh9B+Q;wbSElN(2zH$D6Ft%Dou9xH*wD$yp|h|b)Sm~l@8tQ4%#G@YX=wAt{fE@ z`3P!Fh{5e=7ZY&jMymCM+I6;l^D!zsE|JtJAaNFd$7dghYgDx+H*I@P!DWzor&i)Q?x0* zICIb80MkPt>}G8qRVcpo(rm;a&zpW<#MQ!&8=u3iVIcSqNsmxe^~51F^(8f&t1fkz zswTfSm4yY>e4@{Xm_XqBGAsWN>fSsM>b>t9Cql`Vin2_aPU<8}vNIEkP79~i*q2F? zEhf7$Bq7Trm8?S)vhODQPL?6e*mpz5&KSn_e02S;qx-(D>)g-%{P+9koH0hpbVte^r{-<#mPeVBYK147^he2wp32wa0oc2_JdeMO14xImw`oa?VfCCx_` zyOF1|L{^VTezd50DFJn+)<}M5R+C_m)qoJ$tVYvKwMaH-Ykvj)bUQ)n4|o>L%4Y36 z(IVIoL!RkdcCpo<5G~~_?%4&)4l1wZQw_$Hp8U|3@4d3`;b~F76-nU_KJp~LY-+V; zC7(T(JnpATAKSbEy}21PMi5lB)1t)(-xerq6-oi9^QwMnshmf4WQSRW-rOHZBg(}m zsT~Hs0Z&(H^NWMKmfz2AoxF7K5KZ<_)L|E7eHpI1(7=A8#5cs2iM{1?$^EhK z81XeNRQYmPVP8SX+9!$GPQJ}C$V3G}sI#Sy9WuuylmiA}M)wQ)pQLKh z0>iX_AKKXFDqg=PfQT)qzT31MyR#|2y17gdwdd4Z_#;|mKP%s*l9RN1!iD{AeQ{2W zFAGa&TPEVU2J%q0QC5Ej0qbWVq%%Euih1YiR><7z&xy{Z8v_Flo?8UqWJJ%l9bAIX z_oJFBjn_Vkum{fySe4P=NrVG!o#JKJhi#EnLSo+=g?1{ zWq|!#skGi5EQn#}Qr3PwMxt#c-V@*5PHdaTx;t&h$GAIbk~7-v;r|pCzRL%Mh0B@) zps}o`iAI$A6By=Vlm>EEK~fZ|NEz7hhc&$FA2e+!UjSU+q^HK-=9Q$$9 z^!B-{#!J}iy&TxzRXLuZiWD5AG`rn_l?!X0kmwh}AjxP*c~V7?23l zW{T%Mt7GSZJE##b`7E5bPTHQl4Sl`Mb{m04iSLZX5 zm(j(peV6UGUGqc7him2DD34k8g-^ofT@7b&(-!WW0-M$ZaDt5JZ}{HNZjUt@#uo5= zz8f@(1#Qy13$jCLX+Xu;_fdFbSkl`@a1AW)^pwb6J$ED5d!hq3n0Yfr#z*bQvU zm*q^$h$?sPM`!N66(gh(tLz1~z`9;vE+bId`_Z zqc!M^E+v5$t!QQ07oPi>g`8<@s7>yTNkgki2zTOwa{JqgUVBd@;I&g)M@GYOl*k3Tv9n~l_xMt*xtmL!Bze>oZG5W z-psjUJ9$d}ELLjDe}w0V?Ceyqx}W4n-ZD398#HBYOrKpvQ3I2#IBV9vu`+p)i|W73 zs)o%(#yB}q{^Ya2LWs_`dApcK%p?!$x-vC2gTWG5d;}~W5aCQ(kPS()1s3+3wT+_T zINS>REnZuX^$ewOQR`C2d4o&1L!#%q>ewTpuXo3PGo>_5K8IXy^T85VDoUBD9i(7CI$e-)Ny~@@LWI9!4nj6-x&ta8e#-;t%NekDj>g zQja`O&mXE*8M-d1tzeEa7@@yyqy8sGi~=S&CS$1UO2oR4z~$-P1oueCl}owN&%iA3 zIs4?HZ1aDYq;@!&-zR$S=PYcmSxR7UI?g9Sza@4d^HY0?TG^$nynFSspwaAJ`%Q7L z6sqF|P`#x0VHmgm3`YWLT78!Q(qFw?l+mG8gr$WH8L^^^5|Y-hhzMhlMMI)2@x}~F zilZ&2wq|MCMpso7kqc0WBJEyMY42PmzjbrF{*d7K6^kIZrR8MjooRj*2x```w^s5_ zn8!7J<1v=maB|$FM(PV?qL*vPqpO=Li6I7|daR zhpaci)UB&8z-V};Z%a)Q#u-4`JBMWC?ksM@R2yLteY-g%Ht_T9?iCS%aMBiO=luChtp z%c^RN9Y;l8aeRg;$FYCS9O%{_x{bsgt6|8 zY<62oj{cT)TQk+geyRI3hZu5P!$SyTvy+p>v9(-|<5jZ+sd0KzD$gg!@0(FXK56Vpc)vbIvAY^=2zopzG|^?fr79 zTNRg5ELv2#9T~D=P^V$30>8R5)||6gJ7LDCgs&@4c;uoXUu8pNp2J6lH%0ox+l~j= z&qgDB1%d3C3xYy|Bw;sfD9BuxlXcCq-u}UgrSyO`miKN27Mxjvm_rOh!o+-`w>-7P!o|dv%9!p`ksX>=2QTIXMnsnh_q48n_!Q+c;#7%Lz z0T-m}4lAr11O=px$&XB${{`7poM7)qYkF48x6WSgR|~7*nV#!S>@w=q`bYn6uf!Wr zaSfEMv~=hG`$IJjw-uJ(j11LWbgi9e>N74fjlj=!N9iPovm84sCIkwO?;{_?ME*JF z7m*ZZJ0zPmV?a|Zgl$$U0iX$An?QxX%aH95V~aq4V}ivq!z4q&Y}j~py^Pd#4DTz) z1#?^U<^W%U{Z!*XM!=YqA2s#KHP~na!M_~3ZNmvTK(9N{otAtZCh~4h*Ev9H^BIip4DlZXcY~9FvNZM}9UR>L9N` zStsBCRlYPXhvS=dlMIan9vBq!5k`&rJjU}`sEzGJ%_MjkeYyOlT(X^sf})*C%IUh4 zlJ#45$#zUVC4F1=@J-Y89|SGEnLi#FI=-V@#$n&Td!U!nmY8E<<5rYfc2zwa=zZ&K zZW&ZUkuwHD9y)-C~JhsUayZ=g(M63o1qUJ!P)=>m23_oS^=nmM)W z!3A75jW>psL&P2JGVd@Mvq{^iS}LQJ7Avn+b|_VUw>DX?*cj3w>SMOV=bzd&SUTtI zqKu3-cD9X^=#!tq-)qm&et?42xR*oBW~;DdB6z|yKXm_k2V@7qxlt#lk0S5A+`YFP ze*kD)@8`diEocN6T_N3A!;Bl8yix4x+YB0+3g@(3u+MpCGWyFtXOsKG0}xu50_sak z=3KPUk4+YZV{fR>)9?n=r7(9Fk2Wder;Cb`qAbQ99wp^+a>ytwUM9=FoX|@TTq0r7 zCu`a)y!gkiiO;NZ4yy}dHyk@jvHM54w<4PAqoim8+&Zqeuhm?CPVk3Lo3;3Myxr{yl|5J%HI)v6fn zhlg|8kE{7TzB?HKdhG1qQ8@9Q?Q1c7rwhq z3r}(axnln-+}29XQuUZ;CuA@^2`m*$?64|dr56+;ORYK@97#iMJsQ0gIh#vOM+Z%+ zCNT|EUmwojG3CP_O)0QPshZ(B<-nhLc776P48yeXeLEvyhj4PQS5h4K6yADaW0n?S ziX_DrJm?wQnxBvKdWx`Sx`CE%jKHY4$187i5@0G>o~tQ4x6Vs=wl6n@;g&jE%WV$o zomIV`$8lP8Ytv$awO)7#pTDUIkY!fnm8G2U(zGTSFmrVnzda*SGAjf_^938>&RjH;&Bpy!~7(eSG#CyYc2|EA1n@gxCgT0dHgg+-3+Ww70yNr7?#ptkz6V%*;v- zU$_k}&#G7XbNU^c9Nh=!!*F6chUj$D0eRP@#ND&~;4|Svdk0M}s{~nC_%2wkCovZS zJTb!WdOGS6NR1y)Ac(r|Ph1J^LwzzU6}ZCb&6!D20ipnIF>kgBGgpMP%iQoZ8**sL zCpoUZ-+6oj@tB#uk_5kh1~_L3e6*F=z&PzV^i1fiJW+D}al;6)yViDY8j5!6Jtbhc zLg3#!Ab&+Bkg^^NAWSavHtlSVjyn7(>1)a$e7OBNGxr%tRiXbeRizoub7?Cs`gtsk zyrK6aAah;#ROdudZ};`plqRhOb?jqj+YUd966J;a#>kTWkhZey^hF>wx%>gg1Gy3- zmEK}N-<6Yw`zqwWmaS9Cx2Vy?pjI*m1njRB%MmV_=w8R@Jr(=WTxsgX%*N<)nRA@W zzq~CgceO&zDIMa*jMgVa_9lB{?PvDKc-A3BwTt02UoH4_&KbXrOLA^CrP-g4`y$4I z9G}^BrxOfH@qP5J{#0#YO1c3UR01no?h}4Xz{a8YCJt8Gw_b>aeTk-1hQRUKU7OHT zqOl~ocWM2lDdP!{^~ag(l`QWCY>VQpKE?%j%e{^Eq~q9wwSWhYZO@a``nMc%y~#S1 zZpdcb=4K)g9|BeE=K69|XUbhGR8!1EvvliOfI@Y`1L)AV=7`*HW5To|Bi;k6OkDDD z1{Z0#Qe|+_;}1;uK>Bl)hZ(u1mY#CL?3S^;f_FFWJjuP8zOg2{G80ae3!0&&?6=c% zop&GXveLrSr&F6!%IWv{ibr3aClV%<)9PSlX~52O0_?gZtPT;U>`YuI&O6O+b~E5s z*X!gsCBlvE&F2kn0*Kl3Sp`C@VeEREg>*X3h_0hBUxHF{TEHnfrY|LHs(iK9qk+K5 zv||t7E*kq50N-+*BtS0gfLZtJF_}!)`&%)EW_r6=jHRZXt)ZnP_!i!ZMX!8Ys2eCt zHxae_KUeq;%}L|7o3n0-8$RFscTE(S^dU|l=C}lR$Hs3vDsom3XYTHWLNN+6fZNmv zAd12!XV0&2JSDaE7P}IgkGrm&FkwsjXb?Kg?goOQ?9xI%@Dkw?v>s2OKJwlJ=MOBv`_Yvw# zuC2x@s&bt6b5)KLkjrGfJoIW?EuOIkq921dM7c0Zy}1ivU=)aHqda4KOuQC_Rgl_d zsEI7rQZlxBVnw)8vYr+Q=lVXUIYh)MsG*njj4`A&N!%TrE)aLuciqi^tuM|zr_C;w zWdw|nqwK^%P2_WxKv&G+oNkH z$Nq{$dmkh#fr^az^R2i*1uXz_VG{kkx0d)|vWt8h6@6j*rGBhxK7{_XJfuy#VnBtf zYCyRv1~5n0q;l%&@o0{^AAy4&LFCkq!BV)L)#7OTOWx%V;cnf@gvV&dHB$;U*Gb0F z3_TigH@x)X7imAM9)u5%fEn7XGoC~;n0mcU^>Sjy6kOHdmaTawsJzeQ?v_B^T>J<3 zKgs@!YI1x~StRp~61am^5}rxQ=<>Lu_S!OZ#7Zn-BekQI&mE&I(`ddW8@qXF_+-t9 zpU|Q~P@R4`RT_d%0}^MBGv4%6l7$9Hb-+#+q`I&eXE#gIa7BS@Zlg|_+myf7LQ%c# zeWUd+I*wA89Co)%V5(NJw1}P8-|dMfeh@ww67{UoMb=%J8qXy~UmS0NC>|nRUaaC% z&Qvy~qyrbxg^4fw9cm}S!+Evj%Qh#1a*v&5wb(V5^k`vK$toHDO=2$crc2<>^OrU^ zsm;<&h2`j*c#7?Ig5*6}bVJ?5ehCj#-EvvGHjjjbltg3F=h!jrwZ&6#yTthpCzmG^ zt3e`@TVQ&qJIZowNqHCcbT{@Zde`m3Z^lQPy#*k`9hSgksKawv>Bef^$QmsO9R_AC z!*!yuz~!3{I3UTDr4{tH=^P(2#2Y&Gz?-x^SH^r{99=r6n)Y7$Ut`@5uXl-|A{e46 z*E^Q?rrB4H@1-=}H@mo!wHIyuV0-`kX?(+&N~sEitW(ybx!O~-rRMzHGr~fs7yOfH zmUk8hhc4CBCDbM_z@i4Sz&%I!n54w*tMeWphjzg(6mhsu+qHj?!Z)rGj=!s{Vvy znGsBc`HMd@T>#Mue-;+}4ZtmmCjiF&K`U%h`4)R{;FCB6xa;2%gxCEq25TQ2aJ0Y= zKWF#zxcE5!$_-Uj-a4po+0_U=fv3qJN*^K^6Sn&fn|m&>P~LIKMc;oAo8u56hZ)Se zB^!9-icM>isGkc{Q$)Tc`Acf1p+Q5I>|nw2>szwocc(>hw#8JU!}iclcAIFxOAdoq z-&%xM+tliRj5mg{RcA_1H zN9mNv;&*PjsXE*Ap_?7%J>Po&= zs+_S^N9*H+fQ?#6sY3QfLeI00F+5=DWlfzGgb=6s8!azrluf+RX1mW8x_{RR!RzXy z|L{!C?wfa8tTV5suB*{NmqvP! z6No9kVNYCpeLiimjLNL?5#nT>79rhx-XsLv+K{y#X%k_ML__JqG8Inxk3b=TD#7eB z)XEfpizVEbx?r;(^CFgMbiuCHpgSGNG%G|vi4;f+d|0Lmey;42?-_R0B3JLlM~6B6 zV-=vXr0v%;Df|BnH(X}HC_MPWsHv(oAB6Ubo1kUT9wNPx<- z)!p==rV9B7;E3H6mc>fCF%X-1W@~hDK95PP0%fz)P{(fcAV&jF0|TXu)I=n*xpQ?zIb@w z`inoeIsXbT|K&r)o5P4>Lsf4IGsS*n7LI#@iqhf1;-S5#vZ(bBCKswXh0AcHKX;?k zQf?A9SjROt(8qFYetl6S#NAz!exy^$ZnmqRqSf9yBt_jKxt~DtqN=H_(w+K|O0b*P~Zzv7kKOkx?$w78brq z?~VhCY4ie5L#O9uVD1wUE0=Ysq7lc$Y_$H$unB-$bux-qdmU!%MS~7D?T#)A2AulD zSD&+Q-`3iq+G)NMRo%Nd|0NEk@pezjM7?88hq*>{lk?>UjbJO(@(aA~5!a%CefJf* z4C?(}0!Ao%SAzdEslqIb?Z6aAR^#&x4{}g{JBpSmfqK@>J!XJZFRD&Dz+$<<(Z-7u zCIbJyGO4XHI<@{bL}+6MSBOtD9{7rC%k=`9WdLowN~#H0-m;&1sBT14+x(C?(Ds-& zTj51Z7@=jAvZyNW<@e(V6}vh-K0NYtEk9U@XmW*%O@rC^$-!%-_(n}5N-_M|TAXHE zg1VlH6H4mM&{mfvfwUPkevt*TbeJ@~Vqad9Pt{kkcnY!VT&M5yJ|;n(b>-gyuKsb; zK%~`m;k_|fa`6?!_JBGFSTEv6kNr|!cP2q0*Lf&9l4}wwd0+ z{2=~*s~{BS4c|y}J&vCj)v{cc(oFNk(YB_u?m|j8)F-1iJL|Q|oaP;Sx$oboIlj{F5*}j7M9TA5XXEMhck8k;gR_9JxY>S!He-V=M76 ziwjanJ_Ve8flimwp?{Dp<_gY84O?6r4Em@tXqS)@l_A;6M zDzv3dtTz!NWIs5D9LvB7`a8|d_pObPiE@6_;wx0Hna;vvR>)qDPK)#bdCes*xyY+WN)Uvf=V!ofLqGikP{Nz;LF& z!>QQ`qVJufTK}&oyx{ccff~os@}clAhb5|FSo8%bsy}eF+WVs?low{Prn6ChT0a-Txi;3Guy1`F?c`hB%sb=&6XgeRQq_x1d2Q=SNFMLvQDstz_`Du zQaKUGzrW(APIRm!;)mm-TT9_$)^bvE92v28TuCKi?hj4Cp_FKd-&t3pbM92S5@sC%M)94RN-g z@c$R$tfdL#?Z15jFAsyb`KPe%FN^;xXW9TyP)kMB4(PM{Y`8<_$e~U(b%x3`>L3*< zYNV1N3-sB?s;dRtN$!}jY+W2rc9|QU!C(CY(jq;!olXZ{Oal4o*pM=&X{6PD0na%m z0Y$oCLtu*@QSNi3+lD1dl4ftW7J|5zbE&4q)rBamDURyZqx7IFXYp}@N1kfNdCWNsK$U=?r`W{QuwPY`|HCLbXVBz zr0(8gw1W9e+oi1L^!gPJv3`J`Vx4aoP^Rs$(Wq04W1ct})ZBzFw7(0Qk)Hr1KH{03 z^0xqq(5Qsk=eghE6tP~jUBh$fj<%8_t-2p5YEKus&DiW*HFQ$rq^Sq3x8DXd&+f*| zko;vup_tbuX;q(l5(OS!dEw2$=*7J;!p0AYm#o(wQk;Dvd50ISK`D~`3@;0=^pmGG z_q$YnmwZ0Obp9`fUq|=qPsjbiBD?Q@a+x0XUj$K@X)=7R$_>9*V*eEn+gp)wJs1J!f(F}SQy?T1L|Lq}S z=5+gkKiet>sbs0H;`VOZfgnP$^%GIt^h)=yFr z^`p_~lpA&ov@No9T`VJs-X|9acXJrU%HJXM%jP2b1iM{VCIEsuBvuCc5p1O@GGhDv z6SBJ-|KMO&QcP?K9z;K@Yu_{-ZLcYiBHQPtMJ%7aWb7ijm8C#qJyqb_EaJLQ&C2-R zKCO?wR$w*5Ms2wv=6PM8SH;tYgUm)C;N?x@)J{@PyW`d`YvaocYVoP1c`sMO&5ocx!&6*vX(3j$1t`8La1$)^>ts5BHOYHWn1awEMa;9 z%%1qAW3cRV=laV&c!H7W0@M}68N7DU{RS5`LZ<#6%sZr|%d7|G4`ARgJxe4xn? z>m#_$?btiRO7(=lm59*}M=%Vqi zE^`S{)RU9+D19Nf4JLkFXr~<|+X>R$LaK|3lHWMeT;Dx}sT^F$$9nS!<YNDAI+V`kI($)08|K-%A%t$Lr@=du%5V0DoaS{p}NYarp+g z2N!~ye_3Cf@$!2xHDv&^+D<<}RVMv7VDhY0rNo)qPu;~mWhm5cxLVgBN#QOiqGb{- z#Mt*>4p;TPjCGH z_2U2X69}k*R~Q>F{t^NtciX0g1Mzq&-f|(iKOMRvcrwT);eeG*Z-8q3fQOYP!4S5{ zh8imPSY9A2>X`wc6ov0q_N8^T8xIRAp)|=eguw#gbVkO;9XnyG*LFoJ+*m}t{F8WSY#9?A^CCuh zDpWSpjUxGmsol;*W+5pbk-to|2q2#}J~7HQEOOk2**=leGd<6b=@8d;NY&9Vj{Swy zPJ#Ms%K?jN19xMb%xq{cgfO0$Hifc_MbYWTw+6Tp2Tugm6rX{p2~!k5-c2Ty_0hZ2 zhw3u#P&=GIZ1pFQTgty^LHFv~U)$(^e!c)+ecq*uzwjplZh=QIG4b=%%Vl*(SE(pF z(=+g?g`vy>X4;IQ~N9Grm9bv-YSu4{_!;(%OS;@Kn+fSZLtz9 z(m61-K*@+gh(@~$FG*YE*~U1bG^oVKV=X?$`UIJ>g!kJo5^kNotl{8rjcd$X!5@X@ z*N1x?@!+97*7D>}gmYHZu=8RjK|680J*ope>{qno-SsE@S!ZG;v*}y}zizG^3co5i z+p}U1A)0qSNE(1Gwqp&#B=1FlwUtuy;9`U8@}%>tf4jchdn{RAyWS634Pw6>e?@Q5 zID`{(0S_6pKZTRA1EF}UXZ?iS0({M9E>c%#ZSSUcW(%Zd`D9S1)fQq5YvSDcI8B9> z{P4;oOxb5WU_#0{wXn>>EIlv39nb0}*w1d?oH7qf8{1B^3wDyA6BpY5C?07yN3+1_ejH*cIgh4MsSt7t7>`z@g%$QRML9TtOZ;S$Nl{J-7U5eSg;vO>mV| z0epj&GDb?A^vm|r?i*g5#!r^yHL-ac7v-Ry<4-mu5Y+7FE-(3Ox(j z{u7`@Khk~>%!M5ePjbBwHm+Bkogv1Hk4`BZ@6$L=c!D-lK5u6dp$iweH&g+pxr}Fi zq4Ms%{lC2kyAr?SIN?Gom5FGun=!-eqb=<6DS79{N_RESA#*_U{I-An5dYk+<~b+i zzd>Gs=DEsS5WVJ&k(zEKBn)n)B3C?`0Z5gv`r54}5gT;GA%w=7sBhix z?UL64rf!7q-jyancf6GEI=>Lak)%t^j5_MKSuow^2axp#wU(To3)31eyXFIEUEL ztABek{%v3QS67e{yO=}ozXKU8t5PYUZ2(D$c`dtr&b0oKzGowbp7*Jn3x!;&O50N| z4Ffn_G#fP{%m)qPUpB0CAGzb@z*n%L0(=z2}UgfPn;gYweby3_`gn%}bzmQANY<%{CDD{o8D9s9TvUsUp z4acNkMldpX7+b`OVWa)_OvB7TN%VmTD_j=9b-+yT$CxJwl4)2WfyLC_vfcw6k(AS? z{`ENQ{iID36b5LZ`MsAV4&2<$5|eHm|E9+Iq&~ZyD$Y zzf47kb3pH$SQ(C|E&`@@UA(As@tKf%L1N|?k#&D4#+?%=Eai1LU)G}CPBOHP(lj2^ zhmdQaYl}r1a!*9s5w(KW(LSeypYx0iHw(49a+CJ~UwCVrbba=+LBw10yW;d*4-sitz-yHh+-M%Miy1;qi>tqVi;kt!J(+A?3Vie6Wv8a zayn57U))PMx~WJAnNhR*4IVepZ0k4KhF^V<5);G>nAz&#nbvZ z{aZy?@TJ|+mC05w)U|##~!SCnA=5JHP|MvY$KG{cmTf9Tf zbRfkzzjvP-F%A<~*@gciBW~_pkgT+yvK>HZ3ErPSxBOo~RhVIG+}9aq_>Sp4B^L6o z9J;i6e;-#kHt-p8=D_7&P*uT2L~K4_hn#Cp@y(N(OHqNs=urt6n_Hu=o$O*QD{|g$ z=M5viNBC4T9N^x!;3e1q;l{+9TFiR;&gj8c*yshZGWoZeAYaOihJM%+~ZOW7tjNA)JsG@=v5r zSfH0es})y|BOpp@f+*6O6zw|>535=Fz#c^n?y%*gJv!uMop6i`KFuuiT9CZxW!qmO z%z!>mXZK~{##lR&omxir*Id&=Gca2}Q_JiSiPMGJfKApK6Sviz;;ct{O1cW~A~{!l zTlrcs1g(z&lZ6&5^p@dRwa0bzBcPLSHvj>-RN2)**H!_0_x#_s*2A!qPYEhJzZVX9 zZro{%20+i}Qh)4q;U)&$psfnTaeqy*jVyNmnov6R8Ru2(F8rd{^@Wlv{m26HVK5f8 zaU!ULuXLTiMJK7$9WzKYC+3)77AV8)4B zC|0u)uLer&!lY&u386vLNs5tJ-FA8Mril)~+#7J#7ykgk;DF;A7_zsIB!sB^iJ{V) zlfi^}ki;S8x_tdNp#ysf;=j+@iiX(4(&e%fy(c_38tSzWyQ5}bZMK$lFs)&8 zrYH|~^f8$rS*^%}JwQs%K*?&fT}sp6zBTg9(fO^G%$U%6FkcT?*Ojaf;kHA&v-FoM zfQG=V*>}(dJ#Ze)GL^@RZPf!AM$2!2UK62Tthlm;^3eG0v_R^IgaJuEve&G}mg)jf zQtwSuP(J15-Q~rhGANY#-{|{fW?st8s@=g0T!`!H=dZ6?23M8*;pk#YI zM~6{JDvig6DK*}0V_zCpTOSuW{!ypG6|_r-w!71@@aMwWlO`tlmkDLf-of(3Rlz)q ztexllSB%MY>5sHUL7S}t;cV#P!x9|!KDN>MQKA?nT7_^Og7ehnXWhCegvdutz4C%H zQeL%K{;d`uT{#z!?>Xxq(@T#c2+on{Yf?#Ns8OD2X;ET3JtI3GOKmY#aac|>z(wr# zAO4{KApJl?T{pg=t7UZeW()tKr+Q4=#InTqw_P_vL94wdar5+FEE~h=2r_6&D|7At z1x7J9#v`EdA<7Sr`z^=Mt9`SZ=b8U{e%}of6;z%lvA*7uapn~sfnurgXl~aX+U#yj zZE?Hb)%ettDEhf&XbuzzUn}22A%SL6V zFa7YvI`}sNm-td+&`|WX)icbAw7W-LZ_nNG*KP@LrjrDH047oiQZs$^x z1+dE5qmr6{+pf7eb6k=O`aL5;e@K{hYo#`X!!!gjq~P`}h@6M3a$75S4p$>E+OHLw ziO&!VwY{U8c_>sY8M1I<1b4(ZQZ3B)WPeG8i}V3jz#rs^vcgqMD>S$BK6k!lxR^Y-=uE{$=xiDFb$I?mS!`awI zz{1yd3S-$?7y%R6X+S6_kr=ADXnxTAZ?o^y9p0kZ0{!}XK+z3Z6+XNdRQPpvp@d60 zUw<@FP)Kx<&*X?WuGkeaH|836khW50Ut8=!-#5clLEzU|ejTRdZq_bM!YZ$_bThS) zRY}C8*j2?@w}XI~x7pb87_DLEU#p?Al}y0%2ZLG_HfIQ@+z%IrSk=($@Y*xo$=IoS zqq@__gl`;na4}iRa(&Uxl=Ud&v1lt@@JcbPd%*@{z{&ZT@k2toaZ3z$Anx^olqhFL z`$z}*+5B3*LNvvSQ{#n!zruQDy7o;gJLKY`#8EJ_bwf?5XUy@c^$|ShLYF6v?}yWZ zL5M6HiA?(|+y3hZnii-3|msTGMC0;2ajIGtG{ZScf;B?hyN^4F(?s;s~(f1G4AeR|Vok>y*=Yh&E^>9ZjWR6JUy~J#ageZuwWo$v6u}3`KJC zw*PEqfP^c3tgZ27$(-++)E~w1?PttTY*C`Ps9DmO&r=4jj&swH*~-Ss>X8L20j0D` ze_`$*wzAG6uia2#*b<^MW@i`)=Qcw&J6#SirLmdo#~aNL!Nz~f=mHH`fEDQf zTZdu9vAdi*Kno*Z-h1UwaXh=jS@Hae=$|^0BvnLShyr#HGcl{e|!>oB~6?21)55lKa{XutsZoS$j{Z>d#ixe7Q--G zb@$^+=F6h+27CJAKS2Ryn9*wNKlzH&;qj?s=yB)4Z3| zF6k3VTvUYwxYO>OKG@vg6D$|V8}nHH4}B=_F3##qzjh?kFKb$~#;%rm(Xn`M-(t_G z^F?8r*54lwMLv&pcimtr!q!7#po$LmByG+sN(Hi>MR<^%w#095F)2|4^2?-bVAHvE zWcBF9afq!^m*HT6?o}{pbxp=5TdV}_!W5+LU^>l4Zi_zN*K??f?20ISssLnih;#jr zC>|-DzJ6~`CD#81Ou<3=4xj10q+J}bkcw0o1;|A7R$SbbByk&Rczezx32k zW2gD9)L#8mciy{Fn{AQh{S6B0F1kp+JOS39i<)mWEZJfR>Yi0vxN!cG%Wxm0x>qi~ zn^_&U{3cLlG`gLCYlamFWH^L{@sv_2c~J=oniU3h!;|W1)R?odQWU1Kv^#E`m5{_+{hL|jiZ7q@|0Yh2^nxB>l_as}`Do+-|P!6V) z6Nuf!&L_K|tRGPk+O80k}F%P6fZ z*LtiW1R0bd8V9CBGR?jacS(psOd-CXZ?(Y4&}orqab-bNVjEEP?{}rf;}*%9cH)4m zIjO2`i=;ez1{(5%3f+PNW=Pv_{k8^>c#}zym-XbMoAlb~bYbr8Kgo|SoYY{B+Ay<0 zuaIl!EZl-n7&>oYoMxK3u<#4zso?gPMmW%QAfnhe66skO`IAHoTQS8+FtKC-T!Z9G zSzdp45G31oG4>y~J}PMM)k*!%%#omoPPza0lG?Z(!l0{{uC9Td$0?TAALG1E-8sgc zO?%UQ{{1zx!2MP;rKiuFKn6C#YY~UtgEdrYP4)WrW z-1p!A;gS+_f5rD1+Z4Wsd%wW@jQU9{ljD%EDduCVvv@$B3oUhB-4R)(gd*#=VJMf? zi3s(XF7?P8*h9uLf!DByFdBxT7uL>W1~V_;ytbar^M|I#=o@XfhhJEYD7Ux92@_Mx z+kx`8t{m#}!Hzji{>St+UqXidonoz%EmlCQq zmy|I_NLaS-h{VzR{Jv`YZa9#q9A1A)uvCw`{#iZ;SjvC+uDi}rEgaW*_M=C4B{&={ zK{XF+{CG}kEI-EARBBi)wmdPc$AxNVu(w9bFe*WPUFK^WId9@)RKqWEM<5l{jV-pV!tr+B(m)2axMk_#1>;6bHkWk1L z310fAPrR%3_!sCo%Ce?6Wh`prY{)BfPQq^=*VWj=gcKLQs;-ew<^pzbmN&x<&*tUL zZZc|xd;KV%@yr!ht?}*&g<+8;qEq_ofw{!7?<$r%@_25RA0f7Qb-l~ZE4tK~-0MTI z8TZp-&QvH^fwUn$B0_Jy?ThnyU3uQm<3pAXNB zipBb*^$~Tb!{Vz-gT&2OpA_GzJ2{>>ng|Ambx(@o5J!`$mKA_k{P?a{e4~-csgA1S zM|BuPs#&>7GU@P)AuSCuT{Sq$_20T;W z@hI@;{HbLU!JR4bjaWGdKohT5H-HWwA-dG%S}D^TOMEHPj{X+#^wSCA_&kjhFzj+(B z)?04~Iyv5zd>OK)z zkL^3;4@5%;Dh_8$9{+cb;vXT?+fAe`=|*bSSY1rjb;I75kb+@K@#iGhArHPYHzS3F zJv^`T%UEi4h#K6K22W0^)Ly*rm#E9m=$*rhSjDDj)(5h@_kbALtyr#|eJ^bxAuQnH zuTTRBae_h{8_VA`)@v^WcXu%Qc5(I9vV6N|-9U(R&Fmz#|x!S>f8#pcT6 zWUagEEBu2DYS%B3&ceIm_1e#uKfHa<`P84t*Io>Q;^fhM{w04+u1w!}pG1k6cTA!S zc?fci-)lz2#ONsza?Ok3`+U%FTf{?lr-j!~Dz)U^88p0u7_BcW=V+dBeaJ4szxi(0 zV_xC3yT+5c2a9BrH1_qEl$13k@~6|jrZ_}TkxP3;)%LEk>1dB1$w^#OhdLYzg|M$K{aSi^_#ZXgu0)>A~Y)UkGIC#Q?;pB24feE zl`rf@FSx?5?K;q-R9_vy9#Tn+&asH?JCkrR;z?+l5mfcWbyx3wTE4vcZW=57ml{8? zpVlBxC0e{-ep6vybL`E99Ki+y+@YqGZ`eZuU0-g?jJrK^3q6aQxl}JKz~j(!5}6?o zDEQr<^Gz`M`+2x*!-wURubRr<=Kk6y0oOkK^40!(>b)$)Baa+<)0VBmdP>40QWj;q zHg+L_&r>82vESB2x5n1LNop?*Rh$aC+;c!oS?#e$$)k7gz778LrvD%pDH*Qi=J7gP z4o3;i4adTnGdmtUlrR+LRcZ+cozxMhvPp&mbNa=p5t*vHI4$k==x$e-%lb8~3$KKF z)KnI@vx^=wN!$@5kL$oDNQ&mhNuk3WzrFgdc0Y|=^O966_>J#_kGj}}!wMWCVu*^f z84efyP7@}-c^`7W=JrfM+k@@6vO@OAvm>@eC+@aEb+(l=#&5^0bmIC(QrQJucoaT$ zM83o6y)|Cvr?SOI4OE=sS2^)FH>_7A>uhw%9fK5Yv;V`_cZS2+u3aY~2+<-)w22VC zgy>}mi4xJHL@;V}qDC1+^g7X_M+8AIdUR2uMMf_nn9)lxqs=hJcYF4`pZ&bYvA_3^ zKOBtvzREh+xz4qC0UfU-{0A)h-an3?`&UvqbS68hwZ}bLklR0E1e%Hk`0 zis#kUQp{)9m1NVPII+CGyhg_u=esM0n(I|-MV6Jct&U0M7*&I&he`aT9E40d>bnUT z$46Xu6};DQBmBWT3tR)uZn73rFSs;wZ_E_2D=2;bzqe6%q+*^>dFP12R^1-?XRdwv z*RX2^Ttg+_QKy9-M1#j$K>>CDcuw|YS8OQ-PJ3@Hp?auF0s$ zUyYj<{n(Y3*y<0x^{+Jh0-;qJPkgGfh&W=uAoOOaE^g%_&ORlt(Q7(3B~RLarc1wJ zQW@&2vJ&r%x_!?mVGiOBAKZ6HaA_@(vIr!)rl?|7ttO~K@sb^7+x33dlB1JBFFvL} zJ|yPs(yic-=vHhWS1xU%RVmf>oe79cnSEqjw%B{cJrg<(Pe*-$p}GX9OSLZRz#xyA zjegE_UxKmBSgrSRfQzif;yd+ZZq<5+K1u=2(6WO6|N0S-BWCCsGYX5)wxqpLSO64P zC}hSz@vhKFnOM06O9M;eA}+v&hL0SgZ%HkC0JNd2}NWBL1oP@Y09B0`50;*d~hc1Bp|2 zXV=XY1}G{ZVVG5Nyg|0F3OP4*I>S9Mf*R&RP#XSt`Pw3}iVdB8T;HQTtYT?+qfK`R zH=|sE<2$*^MCMS|Dtps@&-^Z&5OwgCB!oruqMPJPh>2 z15Cz8v4?T7Q|Y$eL-7SihqpIK7e~$I@|wL({E>KpSg%jko? zyZuoaYjz1*fkXqLPr-4ek6VfR;P<%GeV292)2$v@seWlWe=F^)I6zd1Z#2av(rWCZ z@atPo`I>$vJf`ZleAx8x$JIEM1HR11cT`C(nPMJBBxoq+CxefbNksVCFFlFDk_MK-n-seaL8t_gp+UskqYX5YwI*9{!O<2zAX|2%>}#wxC9<#cjT zQ!?M=XQ2~jG9J(C0L!JXCTeyEwH#q5w(c+=0RjO$S%^Mhhp%ZpPuRW zwsSi2@RG!a+I{LU)k5~wlC287rwzzdxWhmsEH4vf{AMtl1cq0kqf;u*9=O-&HJ}b% z`9{J9F@Mty{HpN7&xUMBU>5gwpj`+!j9>p3zfMQeVJFG^-2sk ztN+U5BGUmF)#$JH{M2&5x66bt%M)TWhJI|oK4^y+575tCfnCqxfhAU|w`^~UldtSh&wUd>QSF;CLCWH=9~J_byMaPl`S&WI}f)l|50Lv$90sR871sGOi~P1!jU-ARz`)N#dgWa& zvj{u~8?%yLram*nr3PgO`KN=GZR9m}2r5tl6FuOYAAa9<)9&z^8wdbynbJVr`yA>D z2k=;4z{->O|5QY9I4zHp#f;>n2-v)4si#fm?CJr@eWhP+Mi*9JxS+PxX;eC-yUS42 zDmo@hCx!Hd?@F(^x5xmsbk)9ekZDXlUiTS0TTnM*Dj)B6ePcuRPSIwRGiGS1&ydha zDMz63G0o&ClcfEe|LNAvpGxe4Z)vTNdrXNA?0Z*Oa&NsUE28Y-sZY=GxIqNkUhe7( zXOXWT-B`5}zHh4$l&O8kYx?FaorVZU7OK?pvFF6cy3dnxZ0@O{`0l=aj%4}zYBvzh z6)1aO1YXg2)LCnM=hgSy#o<(}GUKI_Zf!U&sFBO<{B~@EFE=cCp8hmE; z#WOMyhtW^32slQ=cY;6q6Nu_5W?dW1_>kycHj3l<3~`G<&p*Qtq)&! z!f5zQ@6NM1$8TtsB>pbet{-A z{n#H&yR|%0JSUO3dz;yqq&ei?T}u-Inm%UZwo|Qh(*ce1Z`4?BokVy*;Bw!6-@S6= z>=P)T$&}l7VM&DCejI$6TI1X={q@V+dpV78kiAj9H$yyCnDX+ ztkyftK~wlA|L-dhNT>J%pWSILrW1WOHdR$+aZ>AGsU;V<5{V9+7S#}DHK}tI^nNe< zTI=hdg$BWQFIScH9#9a6M2Nv(>7i&N&+n!j?3m?GW<0QqcwX}|e7EgPLSUju)0iu_ z^lSHx6E(I`UYL=opLZ>?nO|R-W;(DTB;RSOg137(ziZBZlIH%OW*u5%9ZCTpNrl_g zXYCX@hG?hyo$hiw0snkqIG{JA%z*IiuYLSJUWR1j=-H_^D8#%>^&AKrgiy|+(l|f zF_luI)*5U=INk{VyYR(|HMC7mu((D&L_tftuLNT_PH z$Z>l*kI-i2xX+YmtvafO^g@hm)@oE$TCrmtqw;`fFN!2BD$Jou@eVN2b2vV>TGnXR zT%T_TOI2zQI_q>SD`$}L+BSyYriY!kVFsMY_rKdn%=m`FdvgPd-W7nccTD{z>f&#= zjaV5>b#&_D+ocYMQ=GP^tB|7_mG-qQRR5qqLL%-A2dB0nJ3^_RX!3ONQ3;x)1WhOw9QQDu74$PL@!uSd157rW z2~Au0zRH)-%B<5-hDSt$juNBw>0{sMDcex? zOz^W^XshRDd7hS7=!-&64)-F$_YfeU}D& z0szoPOEv}|3QbCZp0a7XUHLBmtxAxW=%6P8%)Ik;Ki8^;1VOqDcbRrQ{iC1V%N8g$ z_xgN~q}Ms-62Rh;KVu#Hn|iT-LTplNd>3#RtE7M>W{X&>d__kvm&sS<+sehS+}E1s z;Z-jTJn82~x-pv0rP#_GT~WE?_U7{-M;PX~OU~mUk(Q%VnOZ^5RRzLtXr(!GX_?6sJVXYxacGVR%xA=}V66};r9Rn5pF_GbO1M$vR;jql}dwWl+eXe>DB z))UJwiFxwCNMSJ7OKkvUU{jVoy5iIcL8mYV2=P>^);G$_JqqeE2%2$9pp>(7I*tLt zQBR5gwR1mIPznfaayM_^y6|PMZL3^*uyu}xX>|7HJ$$VTLO_z!$r> zHyjLd`7#{O#C_gL_@7(Wg1t@Kf>+V<``_=SQ%B`ex7x#8pKmq+?gk5f6zKQ~~02|pdB~2aR!#q)HE436i)}^O`evv?v>yL?k zg&00{s$0Yd9st0K{-^vgb%?5Q#%82q&^hp)6t4fNv%fQ(LMfncmjagfT)j$fxKJ^1 z14$iONt^bA&D$_826&US?nD~W46FZZ7nZ65WI2PplD%Q=%iSQ?mT5@jIyZoSDXiSs zth955Dd5&N3Gu#%;(glnee^J0^xYTJ$7{6=hcus&kKAvjL)ZHdpYchSkpLuZEZ|GT zEKzHlJKCl^6%k{U82ARvxpudZE+;S;*m%5b$Z zyi*y&xE|9Jr3Jpi5d3@DRNIr3Gx+i&(}^&7$!jlgqNQzpTrUoK2+N#lm^DJTa1aZP zzuJ~nN;~F3dJc+K1D9yYaa;Uddti%yH!v>ddO>J0P!Mm9b#!)W$!uVU%U?9qem`$L zal9qBykJn#;FgyJ|ISp_Gn7EJv7ClbIG1bLKaxe3zn;%RL>9pkSY;yG_>U=_D(Vc` z3G39V+(F;h9&~28waVs!-X2aq=RepPldW}`mDt-t05@j@5Ru0Zyb{W2K%h~1%JaOsa=l161LlGgde<;{h($D#)bXIuf`sg4EIOa>(#r#PFS}mDQ|>pJq*; z#$=+<)uT5Mu_SxeqEKhSl_`HB>nQo`@Mq)2qUk!8cIoH$%BnC?poL1G822|2f;4O z%=_^sW^!3Of89I^hc)n~@P((JX9veOUvb)?TH2Lt(D$ij4U zA+e8Zf9;@tZOv>M+b7s9i16pbf;Upg#oDppocsBU0g+UcFa_MxLSVdknzJIHhu-Mv z^!}|f?SZ{4aVMWbaC|&S{Q_4FH^0WWj8vGNw4i6kOOO-T84FZzo}r1dZ6y#pZzBJt zX|r_0`#uz_j7-*Eb7L#}v&M9vn9+W0NP^M~)h-!DCr}(HA&A~>I}Unp@}2wP3`ooB zmv0xP(lOibEb>gjHHQ?kut~yGtSx{3ef$9R z?gxM30WAw8tV|Xi?va3tN*3(>2EB#fqqfKev5^62@0Ztm&0J#K9bC5wY!zr z(FY#9!8UZfU^?Fe8p1$j?&XQf_v;39;FxezLbXBp!R1fgUO|v~7_Rw`zfbGVm!WunRf7Bv%Wmcrie0i9bg*7bq zE4JV0aDDZW!Kh`Vf(NZ9{tjw$m6r>BsMMiSUrH2!{J>O81x2!N-_)0W#~;iFlZyFP zJXaAEMx&F#z(|atjMLYaFQ2q^gA-&dcLR^qkHkJ@zn(uIS{r0MJO%Qt)<wMd1&Y_qy0N9zGzmMl$Jj~O^Gzqn>qm+hlTnS9ZR5Gu0jID>nJ&(!ppVX5( zacdCchpW;51n9(9_>Tpzsj~RlJn7lsx62DO&(UhN|J`IT?Kn7;e7-$X)0JG6!O1N# ze*DYn82(dcE9~Bqb!3c#xm@dRs*(9MK&Na}#pm1tpgBbA(G!eO!BF#V8#4*vpg3Q1 z&J?jRINc&DCL!B$fH_xc_HpU@aCUSp@Z?BP4y9NB!0zYu*9+c8ziiUu+}i_#*(S~0 zS~;7}6wa@iS$X~_%5pLE&5n02ewxeT+O)Aco~_8yi3n~pTECf<5*vM-8S2J%1E75M zzL1MywySzwEmH~T#G9%3k9jI_Yk2`e*I6abOYXIwO!SvrUI#uF1&|}#Aac%Tnd3c` zQ~A@a{Y*l_rJdI4YvI6ZV&L%%{}_1NiyB)TD)%65YP*Tc!#&Jk5WN08R)RtaRA z0iN?_zLk)?-k^%fcF4hQ;**p0f@tL7G}Qd*tXqTkG+^bZv{kKd?jetrKiz(iDq$(k z735UW<_KLLsi=AwI3pK``j+>yB8!9IxwIu)jGAH^5^gy<#$l(U&#tghTC;pv->k zvFF}5(&7j<)U%`K>FaJyeiFGI^8Mk5HcA0!t!&So=iy78`$bfITQk;$ zuNEJY8rt(A(;J}d1Hy)nA>Hw?Z=sQ4$Mc}nM}7qW*HVSm7;&kezbl@@NFPpuW zZYqkuKP$$Lm@tCq+sm;RF&lf>3^D)RmRTSr#`KZ~Vv(O;_0Ug={i=TP{n%WU&D(c< zq(GIOE9WYhJ-dcx>k2c>Qd>?t7feX-NN)c|^Ly>d0)yBt5_I@XU{d_(~0qe`(xC^0>h6HUCzS?Sc zixkwEe}h(W79H6L+g}}`++Y2vLCGpz#Id9@$_e(lu8)OZGty2ZqEAD z8hN~=DM79fJ;dL%&O?H`JiXLbk}K-q|exz%w@KCMKhYufD_SJWgwHQAAU(` zy0yWW-MQ^2dAQ%t;T8`EXY&kE{RPO{uhG?1y4SmOPYjoJRx{-XhO z|H=eMM+uw*;DCfp;$i6tdbX8psG+8#rI}dOpQb?I*4U%YAh}Nww7xwd#T!?d4{7f6 z1=-NChu(zv<1zS{#93Yi=?URC=AVpzJSX~ef=uLXM-^~acnvT8asgPCxXF|E`$}eQSK3G#vR%T!dwijR>)f%1SO>@HbR4{6AW z1RUN;m!QDKCjFa3@Tfv|N%%BZ@Z2`9?Y@e%>)iBxd0DpRaSk!cIZHJZM~A0&Ci}cKihI2G*s`HxTfx>gDRR%nv|l794&OOu*n|8DpPBYRay5&Xb86|@xlJ_ z)Qx$_SfIm%@QC>0Jb%$##n+I62@(5Y@WwLzmC>h|{em(%yMG!MsHPyn)}v@{H@-WFS}aeFWmg2fR;O`5x%kKPj<@ZY7P_VD z%svnqhw%7Q7nDQLtaGzVel+qMDH|CFn_tU2yS&!p>sxN{hndMs`H^f-iof4k;Neh+u2&SmRg<{8^9dt4s2pa ztrvdia7Gk|Ie{5eCoKz)D7SuqWq9G5q1C8cKwIVLd4*!PCph5y#(Pq&CF!;7{TmC$ z!BX)y$MQ#NNc$5%zP4PIIA4Jrk0f(#+q$tISAH|^BA2vNz}3lq){DK2&fM9)P~`r1 zrz&-OUwn%Qz+`_S%YM>$MkO0&JE-+Ce*~arD79n=%a!7pB`*<~^tk}V>)SMZiuSIt zc~7g*07b7Dp=nDgPfe98@U%Kpkk@9ur#Z*8MjteTTimfTuFdyuS2BVX%qZ>f$iw_8tt|H&YO0$FcwFUbTD3HQm>f;V#hzY@3?N|AU&I@Dt# zinA-*yAL);v*mq-m<(?vDpw~b0K|aA_5{C*4Mk8NTp#fUbzcd`!uCSwP^wQTCaK)( z2a>44qyLlMkaLOs|Dqha8NPte_X!SNPfJ+s8{2g6oE5nhYGYlEdE& ze`JsVW=iIra(7siGL?!KmJeb>fs5t` zs-7GDxxZ|S@lAelre8ET6#y+>mNtu;bVk`rt2JB|osZz)HXNBX{Xg2Xe-XL%fB_ZY zG<@Tvy!wgBTGTl~Qceor9u0zb)o&z%pQ;37Ex+Pp%92~K2oY6~uDz8Xr>mCS-eT9) zWc=V!DPTa^d%PF>oxNesaF4MDn*I~5v9_c%m{vZz+P_hJu2sq<8}j(tT*qBbx&Jha zW67`dvUu9?@bap!+>|sG`}Dmn&{u4PcH z{Q6Z+!X=JXl6{6Aa231_e9GNANxp9T3o7AHEu>eKv`)i=jn3=af>7zJ&8snlvIELU zHXq|^Ue}7gEOM#BT~zG3?sSJX6!3ya3!?hm|g0nKC>{}W)Am`+6ZHGJ1d_UrmhmkDVTE`jm5Q;Sd0d_Sj0;T$) zi~9WW`cx$(2DeNH>s6JP*NGC2=rzvq7*O9ZFIM?9SV>mU8>g`eTz>llSJ5fC84nIW zyFDh$&(Cv;dZtfAL8)Q)eJYgj=h@+IUUFyxOyQYvg?{m~#jYy!W_~1y_huOh19ALs zVk0%H)qr?Kz{?F1d}=1OF*;S+z4pe%@H%s)u0P*!$_9ElwYe1RR|p>B=kx(+7d)_m zdONfKQ6I!99+IWxbX32tMe+1xpslELV_Ts|tKL;01TWwA*jQwTX#novss#+>fxFK~ zC3CcmeoQ4Si~1~iNtOpz@O3~NyiwHy!9~Z)SKUqpA^@NxiP}Ec8X*;&1%)v4gplk? zEEg}Ery6dhSZ1VvLxEG)(Se57UHL7IG6fe^k8+&JB~M>9e|$<_xSBAt*Yv7ndN@(- zC7n3A;Ah;OjItqN@m5wJq(z-E?@L*mvwF)#6E@GJMTOz;m#vFzTT?vSRQyaoqz}X zE$wGGF4lWszk%^5=$$5|^j8>O?&A0m7}rxW#`Ck$w`ML(#u9TDzdJ?NwB^vxPQ;zr z6~AZSWHxRpuhmO3^+G9kN3g{>{zUpOtZFKuJ0!x+*Fbq-~(zMI<=W`iYVA2HzkRBgn3Azq(%2ePx zU+Pab)s4)AS`jOsqQ|slIiSqi(jl0lx2U2-8=a%IhT?M>F9F%-p(HVhmF=lzbnSBh zt>aL9&V`m!T6L?+Ao=**qTzRNCfHdkL-d~95msN_93MGmexzV=&KV>O{=Sy!R3ZF+ znex^-0AhVT2BO{y+4*)4xuR{Dcof`U)o4myQ#hMJB()T&Hea9fE{5Y6UU0o20J|i7 zjEY(g`(b(Cy#>N(=-z&RF)$zx9GK){5DZ>Q$*fk1KAbe;-)>$A>QCvF`~7NZ$e^)j zH7iv4oN$&*Sj}>!s*D$K@pt4264S^DJj&Qep>;eR%OjZBQqHUt= zj?KO)6Vi5h=~~^qQkf!G;@@0smaz7XZ@Ng_F}AJQL&o>iHQ#9J2$_VP{!mhLkmK@r z_C%tRrsv|8Y5H#{@h`!hy*QV-TvtY*k6EH7p!H~pR{hx)FyHYpFvxj${~k=`84fp$s8Zl}=OH&8teNa}T=)wMEd6 zsT~9ocwcN}c$Tn1x1N%l*!A8e?a7y-C#h`gct}b%N9=nrDiU+)TJr$PilZY%x$TW^ zM&6rVEgXGlBmS8%&VeCO(}u3HZpEYg;TC)DLA4Rpa`(M9=l98Zx>XpwZZ_E|{$ogC zGr$sG?G0w6CO+r^PvrVEa2 zkLT!ep_?-*&ruu2IZSD0u8jvgEKCt)0dZuoD>CR$bo%qW$=s3c_`|o|fSc>Ti-#vp zgP@@Y0=aycNB4?j7JUbJxg#pfnu~5_cOD;3L6^&cL^@WI_BaB63NFuYbp}~hxypHt zQUJonLCUcVf=X>^R#~O1(hQ&=DLzy89`UPVEti+xwwuv_ab6w|(;vtOe7Tt*OZdu( z8p%mF^>~chSII+CgKrUJQB`y0R{%Xh^D7l73(eLWM}Wk2kWmGBdR4E9XsxA-?{YpI z$;0Q;mynO)Yq~CzZZlqZ_J3-&-Fvq;aQ+08_3Zx7_xLI);no8^>~XuB_OA$3y5yym zc*EzLSF(ah20+cpfbZb3i6C)lNXe!rr7N!e&j1^HO0Z7=4>)EMc@8#lsjop@llqfx zE|ZgTaO!!kBekLiNV?oNwd$@u$F)BE0IYs#NHDW&Tktbq@9V`0Mp`Z=LbPq~U*KNX zh*f6819r!k^%`Fv>H)0W(h^CUhro~jCD(lhyAVl+ew=vkyFw}8c!T|t8|qLc_Y9D& z9j(t)rVGl6dcDJosghPjDhRu;oYAhoTPl$H!M^Gjv|7s1kZM*1s_z^3Ox*VqSRPQ8 zhu%(J3t78?e34BTjJkQsPAAcAAc&QW!o1yNjzZmu;m(Z_OtU7cx+KVT4V4E)b}w4F zPp`zY0t7wFom=eA{k(5Djhs=mMmPhIsPjAJzQl)(EUi`|Hz0?P>n+N#HYa3C|Mmii z>rSGU=Tw!@3dc;7q`*wKL>$L#9X=64W5Jsj158*uD?-F0)~29c?*(-F=GnqTdpbKPo0ePh)EUy&zW4qoql69b16Zd?*PEf=-rkxpSohG04r#0yzJ8_ zi-6yUl3eD1g>27mG12R27?8HolL-#=za{BviYhxPK!{Hh(6C^wmq0LzcV(U2%Rund zB1E4^qoN%@I@q~hwX|QmkH*=xmQ}RyLGL?yRqGOI1fhnZKN)U?klfJ$Fi`&5k60K( zz+%^4yJMy?AAf%S5P!E9fMKfte;B4yx6Jwi|DSG|(gYkhYK7Qo=hC+NQ;tW`J`OLs zzHP5#2?4#p@kn^!ANk4o0t&gYpmv+}ewgVYJDoNZQ(#1xW0WWK-I@7pzHHj#ozF)06_m zx&BX_cXpCSa|0)(k+H(KpwEs2AYp?n5{A2HXjr@U?zcwRTJHWSljlvl-PvMJ7QgXC zb-9-Z>#u<5s(XZSNDfPrKs&U`FMj`DY=EGANKrnpN0;=Ro@U>M9!?mc9}RdWeG;M+ zpv0LfSGPR8cW#itPoPs7b78gS9%nHMN)|w6lMFnGJ)({!5;rv|4b$Nd&#U-z!-)H)TF0u zs=g}3c66CH(S^zJcP2M|Mx5f(9i0V|v)@MP(`~4MBqwx3>_ZAdvu{la<{$?9-!0 z&(#QN&$S33HMdSDl-ZbE=ly@Qoo$h1c;DpsPS=3#Ttl$SuiCxf^W$`thYw*RB`!Y} z&UOyqKST#V^`5+vAs?Ru+7#2l3-HNbPK8<$nzvN@4%~P?A@QqBG{|b~0?4ev!v4 zd^9uj+D_OUn7v(}c&(^>%}31F;>C6s3-i%tl_h5`>P9S}P0$5b>nps$JTNj>Ssnn* zbaQazS@o03nqm3ceKBQ|eGFD3&2I$#@wdZ?JZzRj7gL3g*-2%hbfuF&v<35g%Ky~K z6XVPGv;bA3czLk1RxPB{R1wJ`EvgtEmES|5xMC#dyeO_xuVj|QpMcNc2ZQ_AZI(Z> z`I6;K>TOrv!t|QAnQ%0wK#rz)!7cdb?SR&l!pzYknj3x{eNBLta?C13F_78#Xl}`F zW9pND$K2N{WRk{{;Ek^y3rVlK6ARlv{%8-Y;J;PpV9^f?2mP(<3ya<_EZuyLo^`FU zopvwINjiI=tzd?&4rsfi`)a#NHNw30Nzx`NR)`9uiRVPiQcVjvH~;pD4#rpYcD5eN zzxO44`4zmfcR?raN_4-{m74drh?8BtNm&TJ) z%gWZ^+-(IMcIg$4-iVtp=lS!x%ddJ$+y>pm)w3?pPfgfxhPacV)bX4G=h*6aNA$ougJ;P~t&cD1Sj|`L?~)a0_kX^2F>yumxVZ}bF(fKz<_Mz zMAh7*;;B7^e92m!mpP=Rfyu%|1|>Qtp~^Bp{1Wn&#-uHKnP$6Z))u zd$|(ZM^6zjp?rP0JJHQ%OO@E3hc*;N{5D3y=K`n9kQ;Wn+FLF)&$eDo&e%mT z2%3cbyZ-P;0^mbqR>GxIdM!8Ass{bdbG4mda&A`n9s`(tQ~WkG;k~v`XI%fwqXrIy zS|u%?2OLh^_w8%vmw8zOIX_9y_gwp_4v6ut->Uh0d3IPucKn;bE6w6cp%B6clux}n z&~>M4(ay6uKhW0>Z{BT`L>Fg!Sgrr6b3!@&Vu4s65|AH4skQ1sfd{%@u_%oj3)%na z`HnSVzQ=L)5x%reGFhGz9cd?hceUsTh3t<5^3i~Z`8?Z-KA#uv;p1xip`etYxpJ2= zwc?dD$_oY`&6#nJ2u9n4gGh!?EkK*C_V`|wo77z|&Ld+>&)wLhb+B zM=+^BA5@@p0e*SBj40T3tH@u@wG2Rz@#sbpHAhE_{;crapv``)+i@FT;&PS6T5f=k zqi7bRcD>xzmjyslwWq<3>l`bh0oD`Ks0bI!;%RS+NUC zfq0x;+6!GAd61s1i?i)zk@Z?O46*?tlR;)$>u`P6^>-s(il$zGdH@q$swD4s-Pf#? za#n`!A<&r2W7NF-sz&}rcIZ(;et{*GOS?saWekk#=^B|*;YGond(jJKrUH$)pXV9D}d zoRQ`KrP936dR>&CB!3imyaZ0YSGn_`H-x?}qR5@((P7qIPkuL(vTf`mwkoOJ+V_L5 z27+-t?4(7IEsvBF@hL*hhtiUeLL)?KXH_q2`nE#meRfm)u?9t(bj=0%Q;F4U-8TB} zM>N>9_2u>vvlveXj9F4B_pYbkj>#fU={$^1n~X)9YN9yAc5d4D$xvZo@%YUvS^v4> z<2sZS-@IJrfPwkMS&|7ojF9UWn)&!t=SCc?OPhr1sgIlz1{8d*id`P-cgW2%)E(o6 zMzZ?6GPX?}RZV^++J8nZmtrMWOp;cRASZAkuP@*F7vTftK=%Y6foipl19H5RzE&|C zPntU&PP2F_#+=RA<%PH7ad@288(n!)HaNuCv%F5M{P3E96bp3kvL%2KwcEyuwW*CD z-kR^_Y#rGR4W1HYc!Ct#SDDJfiKu1nHzwSEAOk4*4GhAs$#r@a#7k7GorXM@_XNxfbOZ9rmBvpcUYA|he?+iz;Pd#Wg;Py{^n zPQc9qx8^++*)Z=XJn=!{?Bs|lvKPdzD7U#K>@L3mEvS^XF_77T|DLowy-YM3rYdCy zNNKMUNy2Y_xIgxYY`i!mT+iOB5jjuYl|}a?>mus6>Y==${nY2w3y`YyUqv(3Bx&do zJ#IMM4N*v4%rui_cMh^@XOs2;x*L&pT?x|N=JKB!5Bl~DYNCLzcH{IjKMhYoA* z*}fu{xaIByT|cpO?W*9NP|Bdv+vbET^#|Eo9?bA0t*X9!{CdiR>Jo|HcT~KVEr9Lk z)B!TzG6_SA*{%-E&mS7j4{YY!;wsrSh{c&xoXQ@vW z#tN}4$MthTp9c+1OhxeOE0kGu*<48e%j@T5`W1Z7&t6STOwl_7M^1k3{!dxSJ$~JU z()PW#v7NOW56B7k1+B*hbX7sLO}#Ifbmu!pE6is1njdIU3!>7;#AfGSohhuHeoPmx zkPga;V9|A9Honl4^#V7zN7lhcF%sWy_KD`7;t047Z6D}Ni5-}HczK}|L7kRLr%u+5 zCCo+&HvB3bKEP~0K-`qraJL{j|CFr#beGN#;=Ry%9MPNS>)&SFqCMxAZbrU~6vEDi zTb{%rlFdAU(~?=g)RE>R+6w63@mTx<3adAn3Jg)J2`^mF5YS_r3%0fS`*%)pCu^Nq zI%D{X{2?!Tz5(LzP-OW%?zZLhu%&^-A1~vV8{^v}#oL@9E!W$kzJ1W6;yfQmV<3rE z??=lB@3jGf*Eakqv^~&keK=p)UjY$tHe&t#o{KWGF`38S!_&x#DPxYFrTu}rsWZ>E zBG^6DS|Ue+RP8YQpf8FR8A1xu0s{!nAr2wVIjF7Lv~)Dv<%wudaqK(CZ>DpHuK+T< zXq5z(+d#yqT7?#L0er8-&%UHE?a6z>>Ew&9!O9jxxF?auW%37_VHbdrCz2~omW`De z82zRr`7_Mn1!;57GU`K;FzE2ylZ2C9`)6bL{uS|m`$*isn41@FBeQZd)8yN(s^LcD z%6Eh1JcVW)+SypoDEg|>DGTs$10pgHHI9H&jGavdzFL<>1!ECO`3c_$^Y!}6L_+z( zM4~`nICNF_*}qIAy2rBL|43EERrRR+Z6m?i@$6qV5`qcg45!1j>^ioUQq@L7S0Y&6 zSy@u+INsC+1Mu0dJ2LToT$2MJ_pLpcE@Q)4QuH&nI)?Wq=!)EZ(WfD&=A<{&$G87j zIIr;T0!MC#Xg4?fQo4itg*xAF>2Qia<*8>MwKbjE>CqqMVl0-$msXSGyzx){eavA4 zXX$;m*W0@Jc9rJp5&=is9aXct?dJ~{-4)E1(u)8&=*x8=fKy!om~@(6_VB*-z(r#VT|#6P+j{*Q6GI<9OjU#P^_z zn}R)4pJ*_%tdfZeXg1VJz!zKh$|_r!ih?8rzrZ^&mcPBHu9a2Po4gnId}-{TVVh*8 zF9vu1`mG7M7LcBMUFn7vEA%kcs1vwkfoPI6_1*r{V~F@lG5XPQBY>8rDuKEJz8iFU zHIwa5=6i6ilt>(#yv*=0VMuZz%oZhM6azclHW=j(s#tVrE$j}!Ht(<%Iam^R0e9So zJ^$Nh$jFeQSeOEVvlbu>+k8$VyeG|sC5BU6b;iQHfyYUYXD$dS3_SHgrWa@5{FSz{ zNLy0*T^06r36LVt`5$f5PQGw!9k#`-=+Bp+AM{_yFzh`_G>Iq@0F=cQ{D=m&$|Q%BQBr8>kWeK0YAV60uU~W4FQhaIBa_LdO zjRl|jtald>5ko!SJ?CWfnO2=-F6fzHirih&C)DK3h3ya3-zzt3`EEA zR|;#5alE;i?pIamc(;Dr<2JYy#N1ZCwL!vQAtERQDB1<;N-~5C{8)anzMi`cCfMku z3gN3U@06*?`F*ww3n|HSOTY)Y`TJ)|Z~)l``X7@=;!*E98Na07JAvSp07kezd?*8m z!)*e?;@f{Y)47y>ytdTaE3+1!G~b%64qPVpf5KGkev*(BSDC&-E@Wi>Q$IaSv$!0U zcMgTYUFY5y>8pG9?15K-wjWujTbpg$Dn0H`+iVBC9~P9NWt{lMm-A;0LXLH(ZLN*G z6sSczJ29$T&y4M7mSd;yiyh!H|d|33=8h~*W1Db^>)ll7TVBNAt^dL+S zaz-b{_iXU8OWmw&@y}S@Q9mdp=WlBNULILq0q97e=O9jbVHCt?-5uumzQ&tG=2ggz+d3_CT7*$v#J$0A+rG*rDwwV7ew9WP+J`9oI?Y=Fg)0}~_cos; zQQZ!Uw~0Ye=(tyLfJ8S&L=fuWzR!Zk`awN9;AOucdBD)> z@h0z$%3qnBUqgd-X+D>PgbSxhvxna5K-NT0CBMB0NX9`woC2><)B^|h;^k13u%-t2p=PESFF|x) zY5boKJpx|DCx?Gn>*rr?O?M2}boSrki#vWq_g$L|U?--buoo$sV2pjXV8fiK*Uo6+ z9)VOoez=ADqXV)h-F(RG#sWa?8IQXv%!-SXZP?tfS?oAn1VZCw#i??%(3HJ+;K@NpLE=GQ?p$LPlW|l(M|BXB!qh7! z-5|buGFnB@)AEK+`Qtg}m%T-wvH@-ETWxD-#f;THDAJ{2Rw$?Fqs&f>f*ddz`EG2x zdQ3<*l1<+KCY$BG0Pt}uepCZVAZ1JX+__GYM*hlol=xzN`Gom#SMg0S#gr$hPwct zsPy~R6)e*$(Co>)XEtXXDZ(kYx(keZCO%$zqe8j*j$!D3oPCsKpf|ZeX#Lziu2Ivb zIde%y<%27!a!r=g`*SzErZtM2hMicu6@>@Z8vpY3+5X?YK8Yvq+{2U+E+oL@N+;n*DvE)KD@+S`mCoR_{DsGLEcw#(wXOs)D`J3%#|9XHwRP1Wxp z(CUAv2EpnrF}t;~Vj%95(iW?Vfx!Es5?pd$(=U2TS=p|OD@Ye*{0}bzAcqfRuLgQb z%f7IHB1iA#{Oq{KK31d&)Vyb|t@Ct(3x%G}mv&O;^Q!t49LLvjY!20mj9=d+jf}Vc zDn09}9`v~m$W|#X<{SJ|hC6wRIoqcG%m)^Z8+R7?I?lc~&oj9gCrs&n>B+Xz?-bjs z2kZptiu2HuFbdWs4wzv=9Q=Z2{0>ivWa4vGIzmqx~`Zs5QcC z)RJl}sCeRC4AV&?$rmGtOX~QSZWzgYRGw^+W}dEty4dV7?0nVFMxeM3dN9&`A&pA? zWnxdvaPwkTTt`h(4!Dk)67o6zan&xKzP)I_p7tIlBmgtVII!I50SzUn#{*bB)mH_oP%1(d*y07PKNrJV(~jm_9<+$ zRJWvT^yU=Z_%PQ5G{e6v{YOUj50v#{H-DJA6imTo|Z>8#8$@(#0XeOOhbyf-{{JLh--}y<_Z^H5<|Sd@>C-g z{AW;=?-MmW0>8c!Y`mj_zq7)fhRIj1zyd$<|EPQOaH!k&eY_}?Y^m%sX^NhbB_WI% zQKp`XN@X{eB#nKt&LR=YGHJ0hN~MIcGxlAwhavkqWbBM#FupI(^L?IrZ@s_2&+qs9 zw?(Ifql3V+Akmeb~&=HS{H3Kw)X0>23zB?q%uvY zw`b^lM{>LV8EN+fK6BV}{a`CZ-Zehxc^TPn<-rGnRo7nWt>x-|>#{rQsr}?l&b5P| z0Qn*}g}}w#)FJ-M!RK|gxwsF>Km8QfC|SQmc0F7LdJZyc76fZ?=;_*@m3SQp6%`FY*%Id{jC4*~RM9SyC?y8t%T`N#6_r|-BuLv(i+^MT#L zFqRQ1hDYd8&7W#MN&`vHgev!oD!l{F)i{T zCqC`;x#%^9kiE0o?vYprel5-GdYo{QyePk){khrq)Mb*ZLc?0$``ge8F~0}wITv-5 z9^E_2=dL;)%q}cDt+dzFHwBtvvUW5Q3~7>Hv`vpW5_^$+OVE3oY}buCPl~zoY2*4U z$7f>z38IWz?H80(EE@jgvtGX!e)$V=J^#t#Y%gy&J>t@WQTNpg0G~}?l2-7Ki}SNm zCAXmOg@pr$h^42uPFC{BELmIqJPZ_~InMP`DPtO(`+u>_(W&d_?@N%({|Gr>$LcS& z9`FI=7Y}?;d;d2%++51XO8^;V|c>uDqp6Y@jM3ogh!`qp2b#Wips z^5^OH3kQ4w?Z!Qs=XEVCiSanh$~UD-R9Fa**YC?-Zx5tie}(Pb@J)+>&rduOu8-^m ziXJVwgzKeKs@JaQ+)2?!UBhC@2h<}^mQkurUry+}7oBvuc51ca1=8&}VM-rA{uveS z@PGs!-`ypr<%QnOI-v`NJbn>)Ie0&DBCt-`NAh0V!(Gm7kgPptbnYDS#2)UfG(A01 z{(?KKCTb@aj(HS$C< zK7l27o%k8_{gikIXG=>(HOqQS-u|8b@DU7MNLA)K$WI zx@_NqA6~{R%um;$%82;gTp}0-Kh&Kcv(_kSkA{!ekA4ro@LWK#rA0iR$?=ji8Y_G& z*n7`2%geg53!h&X%huej4m0%H+Krxn_>$9n4NYEzKq9C$Yi(W8Hy^}H1d9h%J$m35 z#Vs5?r;cssZK#N+Uxw^H**L6_0@YtN&x)$F>z>){_EEXXCJASF=IOX?; zdRUm4$KI1}c4GMMN2I}+1S)RHEK1(a-E)5JH_MK*{HpyaxGyyr- znrrshm&Af2_2IyM?}()S)$mhew|N%NaS}`ceqYpQ;HqtbeAHm`xx#(i{8xnXn*8$@ zp4SqPQ7`XLtwx^bIaXugG9Pjvs^K^BXdq?nR$$GsFs85`f z#OV*Wia!s^n)h!rtgZkI75~bgj?CY)P=ALeO|*77k4)1{n778??>!W_r?xG(Z~%~7 z$kS_-S1?Cb2e%t^2X?VwnAskWWEI~M%*uS{gV!Y+$4_)yl3=^OXCi7ZOpmNq`+w%s z+c#x#l;iEj^4{YK4<=s;MZ5k6SG$O^PKV9|ACpRVEw7%GRZ>ibKZ5PH`pw`DH$Y!| z1Jx;&TFCm;bv%(a9;bScUFy|XqY>9pk%!0O5btv9Mxjt$i|l*-*cyTIrvg{^lIK4) z@*2(7iC2Pd@DvK9TuxLRNg{hsX`*$4~ z;G*#9q6If;qx%!Utd^8^ht&}-E4+k1JyBgnSY1~z=9$FpuF?R_94#G{#k|M)1gk} z!%*3o!IR3?AU<=|*32@Jp;yRp*=6O#NC`L1&y$J|Uur|#01;Pzun=(Jcie<7GsIaB zGG9F|bs7smk0-hs+#zc{Pd=`8cSduyUi%sQlq+hT-S}4cA<{%xi)ef#O*urMl&5d>=JX)Rcgm$p0`m?^-r7%_0#nlYvKA$ z8lR4KUCf=OF zfQX6Z9PuoFDgWBgD})By^6uk4+tJPb@W$tw4ernh%Z!jNJ1D*wDE<##HOg6I*w@i9HoZdL<>Y@ER4%{-8_dd1PocnR$m|W|@V1an-E*Vx~ zJvo8Ody&N!RFJX<(dH&D{A%80@Mirbnemp*C&-PnhnJsGYL5Gi+sdlfKq3|) zRXV5dANE=Iqyx&x+I`z9c>h4P{%6?LI?eCQemT47o%yq$;8x7OZ32#`f9onB=gXFv z-~0~eP2ce$(@(W{W%^rWU}S+_H#7BKa?bYY$KR89jDxrk5>A5rv0@C}nTrbMo7 zK7C@YG@NAkd0ft<^LOIT)87{mfH)xd!leN7wFQ0q@5i*w8?E)_ju>iRQdoRsbcVZ; zyOXCv_p0#N-R2R=o!8V(b^=8XYrfE{o7(^T@9+$?#*Ko?yC?Xg*v_? z>5d6j`;l_H?C*S1%MjvCIY%oO0#)iNykaLWPAmi^313E(Ze!1z|K(snwp+tt9-XM4 zLZ@c?;cc2`n24u8|KB6&De#9Sp1Swi2B4dqs+lTs7^{y4J4IlPlHY%~JvctaeGPBF zxG{G0Pzl=zB=#b#JIMe)EjjkgoA-CyaaUj_{lUM^q}xe&v7q2mw@AgU>g%t>jkgs9xj{QsGGsg( z9f4l+5*s`VD-b$d=JoA<061}^h?L){2bm->p`^KlvzCLY8 zYo$C5TKag~A(wK$MK#eTqa-yk`Sg?DPaZ#XGVx82_JwPA0^}h(ud_w*2V~DW&8@e5 zAxdO1$~^Y3tay=V4aPKyuEMg8BQ*MYlVu!hCWz0y;=W=;o6tKFJuR8!W?Zsn9P6$l zcC}BAHQDr}Kaz?(G6UG^1X+J_npn-aU3XCzrQm2t-0;8lnEw|>b5SwdCP7MrDBXi> zf1Ble@++SgGUQDmJEi`Edsf;1ckWq>qNBtE#EY-%wfG&)EwIu4>~CzFfn6f^TqAeK zR2FJrNUko2Ib<8JHZ!6T7z!CX|5=+y$G+N_q>G`~pS+0mI*D=a$8)&OSq zOXDv%Go?_BE33K-Ryp?} z@*BG&x;ohMJ8ZB2Y?-z86tK*~|Nml{b?gvG?>|{)A!Ru^b9OYgQAkwXpc=i4t5JfS zU6b>6=t7T;9`R(s9;vpz`yJo{UUPZ2*1Q~b;MeTU^T#BUgIPh}SXra9{!b}AFFfm*)V{$7ql zb88EdVHga8d7wkxX{NJ}hG=z^!E>SR=h35MBbh6Ehk`Tm!tkjx8m)xfg~{c#c3E>V z?-c~^N~Nh7JrXokQNa&`^-5#b;^kEjo-z>_XV-b>vl~6NyeRoMoVy^8&+=9 zk2P0LZ(L;` zZ0GcfXm`-{TP5%JZEXC`U0NLz8NQAmd8rIWR~xT-4U%}(>1{>u2hH_*xalzz!#vUq zH&P!ssV$CPhBj(Qqt_2)Tn{eY1vqSKAY1B!42j}g>r;PorH{vA45=X$`i1?UgePFeKG@b5fdbvE75(@X`^NgvAsR?L z%@Rissr{~vYo2Uyq$OpSW`>ASY|R0)Ul%v!3}BR6&8Bu=Y32T3L{BbXC4@!&^P2J3 zfvX0|ewRfJIcy_8|5(T<`&}^{5wxiRJB#7;LF(SEimy_kE1!N7fAw$uw%-0{zb(7} z%l##2>umP-_?#HUPT22gAf49DJ%gxQf`<&B0SyJcpB5oySAdUJkX%NeA+d;7)vrce z9!-eHc+8H>a9UJ54*DXEURSUWvVS+!**eY!%l?$magt%%F!o0cAv|NRb@&I|`Z2f&qPB9Tufr9b{ zmhZGestWkpYfQfOPV8YJ=@^T6=wnh~gJDTEVegkzd$! zYFSwAfiAcD+0;KjCiomMW)%EKZ`&LE!YsB1Nw@5-&OnYsvIXCjh8#he$q#k-*V&7M z5rJhMYg1&RM{`hAvV&R$^|6{eE<_1$(-qjU2Ln`BWOuK(+qW7MqCf?) zc$L9G-n>N$z}LotOUUyc$T&_7a%ft6h5xS6gRU8>%GI@fFUkD{Fs+Ja?R|u%ldJS# zRTGWF*z5n~#MQDaJi%I)K6H*?7zl-Od9{iq@sz;ade$6BnF$Il@|>lW(CT!Hy>%SUBxckG2CBXxF<7L`1aH~Ki0A1tdr6N=4Eb-JQNUumaW%#IGsOJqO0bELY-%aa1q4FVY> z`7Yxgbo(B^qqEEfQ7%eIwdNi5{nDod@$$dQ>(&7wu$Z@QAo z&Q2|(62%aYrM@jlUsqBc!RN3|Q`}{jt(~umysLBFA5{0qypfPt2mu~ia6Z15q^VoY zTs_|NSWkY3)VSdC~M|uuP`5Y{O5)e}o7NB7`Mxrt1v?2qvKp+?{nWMeQ*6;*1#VwO-estH5ulFzDOp0e4 zoW&Tt3jJxX6zs9jT$^@XF|rjm73>G%%2ZW<;nfp;nP};jc#TR8C#dcOLlcF%7*=8zM8YufycO1?*aNUacT1F7K?%vCtemu-~ zTAQ;^hG94Wr7g8;MWUTGIgbyQ$2U%@6J=-ZBLM`tq3<0{=qS@ppIOlD8yIF#|LV4-Dkfz8gsDE!iypT7C6JE8%&-IC?9;C%K;Ec7)6hfs$^sf=Kd4%jKP2>glP%xXndoyTWds#?I zjj#Eh`Oeo64lXFQrlq)cD6pB?ICJxB0B;2%er=8Wp83XyKwZFLI8E3utR&Zkq7q04 zY+IjX-;Q5dQOa)i=w81-m-crolfeCQ+iN9C0vMb_=lxILyJmaWQ-QzMo#2*Hp-c=6 zkGe8+kvm3kaMbLC5n71Qt)AGdj+utmp1yBAK(%Vd!3kP2EG|}ugLSLqYWJMQKGbxo zz+x6PGhjVEMOF^bnOJ>p;-iDI)E55OwsNMRmI_g%P|8X1UE@O|Qk*Rr(}`n;fs;%M zWZMxcnkq02@>g~DH;jdyeT%2sA?%@P1r!Q%-F2Z(_U6=T<@|(gNX9U!31`U=ADh3> zVHeq+3-MkQMu0|B3GvM8A(axC1MUIEcG)&UIb~^vk$?m|1AwW5tRVltGr_;UD0c;_ z)GYBZnICb&;&;LA%!#5&lC~|LXEdOUWiB4@rZ=UpX0%G&GSnGK$f&K|WPKfOD=3^E zzoW}@-Ut0j+sS7K1m(|k(4fq+7p?Vpq6KY>!*-z44k(dY`RCG#ay#)Vuc&cRL>_o1aNbA zzYzMdFsMeD$rH77n0hCSSKaFBrcr)re0xT7$p^7}N9#S`^(PMXoh9cgT1~wBXnpFa z4y8Ls@rc%bSqP8}Cyk$_m7vA*fMg7t?QGM?akpn3%S^zhFVrE$wLvb8}S}&{xCqN^_?LEYiYQY`_CmKW*!S*Qik< z_j)&mUT-mPG)peolt+B7N#aA-n5KSo(x72$kNNvcElg|HBq50?Qj1a)hHzM=1%ukPB&3Bk1f}N)7 zl+vQnfqLaii5KRgceWb$sTL8iyf5pujbO#;KkgYep)Uc=LBQsVPrNii?5 zN2-N}Bt;x{xxD4Yr{x5)@8e^IRUS-n>&vHih}B(Oda{t(uOIJj<-qWl@G%!W8|B=^T%FH!38 zTpH@4phM@%zY?8W&$Hb*Djm2i4~p_T+ZC8euR>G^xe%yhHHSA{c1W2=t*VpS9h-v& z;%3?5)th{25e#j_RnaT_)PyJDN*KjRR7+dJRWQTSOvk~kXGRdb`B_Oq`&BeOI{&X2 zpc@YV5#jy?2>~BP2mr!orw>K{Wa)fUx)tj?Uh93kw7gP7ow3rFt$x}gG;H0Aq8!=1 zJX{!4Xc!Bh&t3EL1o?-1r-4x$0@KYRp}7;~Kw;GvXe$rTgi;;p6vma*S9!{63{8R` z2C%lK_XoQ55$S1dvlj+MVNp3^AepKx>fVw5IJAzkt^|m0yg02zd~ikXQ6R_uA^+>i z5@&QC>_uDgGb)WGyrT4jRg*B+24};C{J`XUrgkVx$Zazp(8zoyY~{8sj&T>ZJiA+B z4JhnNHk(tH$e5NvIk@DmSHIi>%fPq441Ol_(+=3Xy#rJWKnqOfhU4l7MrRuar^DRo zSsZNJj4NhrE}uL+Sx|(C8Vz)gY!R)<56#w%Z6V6sKs4vUQ1&Z4hMe&VA+t5+Y!6m% zyr@(4e%l{O7FmE8^!ikYf`Z(lOv*Ze7)CZ_bk)J`y0lyKPIDjT<6PXGc_;+puF@pg zn#8_?pfH)Sz_mAaMVVKk@*ZxdTL6=j6_h()!$2OPwhu-d!Q2<0$wmC9?jR zO^DBpd&O3~ymfKmJbB*AVa?hZ)bfzk_6=WWVQNpNzcm~wS?W*#rv^+-p) z+)nics!dDkA7)afl$7=XaS7#jO>GJFWITK5Bhc}JJyV?TFKM87AAMjCqKjm*6EZc< zC|jcMAxph>*@sPi{dA%4B7U;xEQN~|gqgokAop5v|7edY;FRt8t=rR()&))`tUJ(v zBG=u%+UX{^wA@wezqPTo5(z+(U_QL_Q=d{ls@$2q;T%0krDGz2=}gk~*;%qPB#1Gy zBGFphHZW0C8(FX-2`-)b{BBw0wIL^xN~s&fDToFp^v%Sa1)&YgQX(1<;%mLYtESiS zrR=VS1hpBfUMN#^kSQ+>svy)U75Y9tKY9?I;llgWlS{|nPtoK2#JMQ$I91h$sP+I= z<|aK`N;RmWMOX*4+`Z6AZKy#;F>es5g1f|Eo2;?Z03h*lFm#zQhX!LDmcqM4JEReS z*F9@>3jV1%KMiT2dJOqr%F_A#S+luL^rFA=F)?>r?lCF8S`fz zdzQ7B6>G5fRgLsSI=DZ{1ADP0+Hi{UMnL92uDKLCW)Db9D0 zwTaq6qpwTxARtfk)=m`Cpa_Ou*m#9pA->pZ5OaJ^NHLR(Br)V##n=KPko; z418$@v#~9+7J&9P6Q2@tU6@76(hIBkj|gr#`7SH_6RX0a{H~W^s+!~Cp0cPa1b6Jm zD(g7a=_lnvX0cVw>fAScg)FiyY7?O${mt`2;+O)M)3UjW!>h-y%SL+2Xvbb~bsSaX zEAM#4U5QZ<0o49vurkH{K&PGbY*WBX1gmAmq~u}eT#AQ^mv z;EBT>>I}rV%}z;tzcgfCikr$3+-QaL#P==_#}GwE2JmmOeGEZDQ^Jl1V=9C-k%e;| zBJDkm@4%>axq1C4eQrDJK7EW1c3B0n)l(u6n1flmq8+h^+5Twsk}WSU`%z7<5lKtmAPGP~5Jr)hW~q$140Vqu9p1q$&)ROvhUGurl)=tix<@GA5`cjW#0+&m3mKkx@;^WTEVf{{CQzqV_`990Kub6H#ONYvBwo1vrVAzgy>6-= z6g`z?pyNh$J|%{dx6)443sGBU#&An~EdzZG8*a(_EL<6ZKE_Gd(S36(AQN+(3VNw( zRovQC6VxPJv48He>hxcjQk+KX2e;6g-2yE& z_loI8G+UUf5l|vcb+glLJ8d0-c4v5xeCr!1j5LeeKrq75tZ&9mf6M__kyhrJ7q-BxB zc3r3%%BUMpJe?F8fcIa#jGdW~K zI(Y9$By8S-ykNk1Nh12_lc%XQNqe5O>;PiK=^Z)#KPv0;qsLm$WSE)|%JsMw_!xaP zXr~rG`5th&85DVM!7+j5ce^0Ai)gxXA`udx^v2;{Zt+0r4%%otC0U9*dC=KBIbPTg z?oJyeBqF9&g^f?OXUm~2AYr*0oHHWRWrsQ1BFU_fNgNbmQkI68j`FV-lyqzj&30cJ zbD98kR|z+q+ZDvCj^aIee)%G;i*l3$aH>iwBO96wYGhJK5}EoYK1k!W?Cdhf?Nw<0Wpn`|SqThvB}Qc7RL82e($pAh$0^RR>>&*jvT ze2=Swdl(BrJdnnwsy^betiXqD8{GmAg4S|-FI#A!bMTb9I{eP*r({DkM>V#w&1i6= zyZ%#?_pQ$Qi+GCk`=-I!nv%T2;g{B6Xk#}d@o*c!Vdn5P-gp=-c>6xwlcDCo|FNmAq)awFs)pT5q zgI%haW77U7{Qq<4*rzp=-Dt(2$VQzi!VI2Ig z+IoobGI;ca>gT>0tZNknZJICf+{fM|1;<2Zp$E*fT*%J|*Ey0aS_F zOs6#I@WS}@W!pKX(O_iV=PL36puN@&=!0&k! z>-Em3Xl?&xc=!UTz5Fyjp#G<$_u}#PdYJ1oFqgNJs=6m=c99&*7=|T~J~->m2^kbO zB+He!vXZb0XJnwYEMrtXO7hn`$8zTM*|G)JK71% zxZpNnY&30jfSzsc_I)u^C)Fu#y*ezE8HAsJUiy@&C&o|yN=m)V#-__S%wsNwE_m@o20=I5C## zp#l{E`MsN{kDN*M>f_6_J_N|OXULU9?VCdtJWh$*m-17)a}`c#Ryw^cUgc^Ot!1-* zd$wZK7KhGIZr;2>yeqK%&tURy&xt_!)^cc0Jj-Lp$^bvI?zl&#X~@Xg-9>@1>%!|HzuKrei^H;K-Di_A;@)~#X| zFLpE58b!!S6SGJpbuvS;mRuX)9H=L_@9T32jtX z=r)mPyVHjzKXE;1hV(?vi#gg_A5c8|c-7@n(d%=9nX3LxFIt*bXbb1gJ`DVRfKFH$ z8XFpRUd;`apje#erMyubHi3Ar8t+%>@;tR^B_leCRB+|dfp2RA$ITDmqu#!hp9 z7x#BfExVLe=h96yY@NZe9c#*TZkc3Y4Sof-buJCPaO%&#V$$NHarR51(pcWIFK2@~ zp~REtRxHy7NH+9A7hT~Wuyy}-kGjIGBoR1@kJsHm{_5Vb`cb{o5Bts8#S~Ifx?cJc zk#-f4hD)axkupL0J=jJ8)bIqZOl`UYF~@3Em4?$ZUy%ASX72PS(%?6IE{$zV@{G8~ z%-CS#_o=~N559-jVGhPM?S>nq(cQ@Q^sE2Oy|;kbkA za{T(C=P0XaMdL1^OQ}|I>-RPn&gp@4EY^GV+g(4 zDY>114b1NSNr{*p6No+2LwcV$VLrgxF*~||m1> zoK>3Bl~@8ZHFJ&M7P^LrF>s5_ces5$5N-vUf=r13H^3%EE+HLYLm8;^BdKRihwT#r z-MAzDayad0nsMJkg2+8o-Ur>PXI+@W`nSx?V$EB{{CHeuf|dD_d@S$y@$nq3{*YZt z&b+RNiA92^gJ6^OO&87lugBxZvS9AZ)r}uoigEYZ8~$6fviu%Uk=dMi`945T)bM>R zrt!BQamM1zKe8c!{XhJFH5=j%ldXYb;$Sqe`d%dhn2gU#&E`djlmVm%ZJXiB$K!*r zC8u!;vR|3yk5a8lz)asxSXoYvOv~#@X^c_&lnQq+COg$Xcb%KV8vu@1?-$#N8*fcF zf|2QYX3hAA==4pt7da#{I2 z^jMgjXUHNoB72%e1eS8HN_Q8J)}<2^YTE=`DRl%e&Y0(`_h-XyE@1MjZ6hdd&S+G(V)>jBCGnyqyX(%Ag3gsw{_0zu4G+xFojwP zD}haed4uTDV!_R8scM6HK*0=+7Bw_yBnQ4shL4KG*;S59!&xqCK3jm@;e?s~o(Ttd zGD`d#exUePglWc5k86*C?#V9M0{p8|YqxIvtoyje9L`C|9Z(Z{rH7lE%zST{#&S}h zZe3iB_n5sej_$ZVzIc*ko<@KE(+$*5+j>gC@CA=d@T{3}dXd}pZC{d|Vu)qf^fD`%H zf|S3XF^~c4r4aemY)%7r(qe`GS2(koflWX zG)5~bMINJnl1Yxkx~|o4nRd5dtsY~Lea9cmd75vouf60Q1$83@aRWUq2AtJOAR0s<^HHGiQX!J!UjrcF-*0y5aCW+{fTig zCIy7;zH53TLWTwT6AQcp;YD7vTLQDRw$Zvx7>e50kgZej31NytAX1x-^t1`IOzJ0> zc+&KP1JAl1q0}MT0>EA$llu)Bmy79{3br>}x>t}LHqssC?}=+h`h+$n)u67&E1p;C zs4ywRj&FwhaI$lTKn0gdjaZHd3x zO=>dj$bc!MfEzE7noa8@4uBifq6tNBrp=k-zEuw+lB;-u1TU@oUM|Bvcl^-!j1#=r z-qc5QB0wx%4gp&D=(@#jAySlDHsC#OHit(5=G%^BOxv5qdOdtrU*3FJrnGT`fL=GQ zYcW8j{9Gt3S2CwAiv11%PbknP{q6wnk~_b*1)(YmjOCb zUtEZr_G{zsA{iUC>T5XQ(yHkb(kNlQc*29L%wxc#K}c`&i>Hee><6Iyakhp8E?Qg# z7XNyz0ZgN>_{gIfp+c%Jf1KngR}N&@IpKBH`t}AeS-6K~FJwDv97F{LxA7 zLcF_@>1c~Jlog~9GWo%7mQu^g{MIm3nBx5hJw|oS^hLYI>BYmVBZ@b0)9ia{q<`O~ zqsv>NBf$GmwY7DYO& zBeVw>saZ6~A~)m_RaU{MRL&FY##j;ghf;B6>+^#VS5^9R8s>WkH{OdIv@ANqdCpkZ zE-zA;ozW8ReVM^zI>n@+4z{we(<8&mP}DGC@Z2T#hNnI%Z~omy^hYfVHsoTSnikHgGz7*0{NxLHRvwsKv{t8DY00OSLKH>h6aq1C&@MJsV2d@Dg z{mOo|t!vHk8d}?=9gcq??acErsEV}-JG+>@Dv5A!*Ac?m8!dgx*@nI1-CoP%#bY&L z2}+PRj`wmK(-ZoFk}|vNFY)YBS9{wOI)HmndyXZ)a5&Q;#H&vRMTPEd5&~U;+c)FR zRp&iZJ<5k)JzRb|%*_Nm=pHKZy434Qjh-FqO&uw%Li{zXw5QRxIcCzcXS36WA*e~U z=?BviKBpl+L25Tgwb>h1US#?{_Jd+bwh;zpR3)2|o^qE8KKwOZ==TF1NfCfWO9m80 zD*3`rr=3d1F9eTofH#~wG6_fP3`$3A#&^qME%T4z_fLL?gvqE3y3$VM7qsb!MKH>? zaO2kr-Zv-KU&;A=#ICCkn%MN+6aQMixWTJ0YN)f6e+>4cW$|}?DzCaHwV5MA9k0He zeGZ~T(nO7l&?0*rIJRHt2Nr!n&>RF(P>S>rFHzlN zZf!VEM6)!$AXNRl}1MmB{s+0KB#6)zj}2VYXtj-KnwY(HT+rF0Pq4 zjiJCvxhoGqac#wHT8Mr8o{hmouh0s zsL@m>UfOFXpc|K_HZy~|8$Xx92%gg8>c^`yFG#Sr>X@P1Kz^iGY7yXMV|V(MZOQ=a9m@IoIbHBa4A@F@tMJ_wVn4bD;0^h#HE~M7`ed(u=sh2G<7wS8|>v( z&w-Xw&WEW^eQ;fSWx%kXxg#@yXSOHYQ0{Z~%j$J3itKkEEV;bYa9Q@ZvQ#9OmSfu0 z3nxEiR~3`Tfv9}LYdHz5PciY?bafiX^t*9?S)*f5u6w1{!QJb!=*lL1oj}C38J;Pd zZ7#y3X;{~WxKg8SBgC8v-(^qy`vCwz)4NdA@OEc$7JgIZVPF}yE^9O|7CQYJ*n=6K zwReD4QIvCy>Q9kY35c|V#Vr5U@7uTV%szFj@GKyEq***?+q4Nq&5j6QI6J&-3GsD? zIu4D!rj6t|ptP+4R}c#w_NnaxuS9BwmV-S!Yw}*LEac_fvY*?}4D4uu0*u+@ zgIztwK1f+s_v7Cskix8u5CP?lT(aDBf}eP4klKuEq5F9~(CBCDIC`c&QF!BhSMqk9 z7s6`%s^eC=Ik1FcZ>X&QR(^wlj97pE<+tRQOLD6MQ?I^w0zOJ#t5FphG+BaNU@1M# z%%-$-k;T}$YE<}g3o8;22n;r6&6Q$2ni+j)C)M>hCpn>u>~#&ug{N6FdA+j99xh#G z9wTjW;WBrYwaY`=x$3wiD_zZtYhIv?Dr1+hR!aTYn`FW(=z78 z1;vaKpOUyVv+0e3_%D`$;DL|n1sjX5%L>HVXu$E~^k*{M)Tj2l0>SGFNoQ-Ul3Pxu z4!pHC-a#0!Z(l6z+O+!y2&{imgJ2gKvqf{VgUq=t^Wl_nb(v)v1eR8msK+O zJ(F-s?;~nSeCu0F1h*V>&6S?@uS8woov5>HNe&ikZO8Iok+$aZ77==b#nW{EQ($-Y z{4IBvwPibl(ADg7yTbCkb`PG((6qGLX7W}k82z23wJc8mLHW9Orat@aGWlu~V!#1^r8jU0rnU>4u(O|o9{q#D7GLZ-X)<8y z-tUmFC0GYI2OHqMzK=sdck|6WWSy|>v4wIsO^wS!7QXweH3s2dT=>SqwgMiqw_x;- zNz(STGj-+nsaIefibv;hldl6K+BH^({2mj*%vrAq+U?t&<%^Hto{rzP-pQS8f4^ps z|6neR8vMQedzt9|DctPp=$B(h`U_HNS&ZxndG9;EF0`2v zw-IlHyohUB%0JP9`Fb1T7(jhxeShi8Oz;MyqoJs+cyYyG`lfsLUCLs>t5krwxL=I)+m#Boe^a=p(fYmQTiEJm;p!vO3XBk*zgTHgEz~At#+{Yx5mx0+ORB0X zQ7a6jgf*2NrL?ywu~v^y($CfP2nB;&sskoRjf?0D=jyyinT^|5^LD3weBUL!{tGwT z0=QXs8#lWyx7EFjrRnA6WHnJtUgej}WMnO*8jS~tIUWb#cKs_B^s*S^VnW%P+*a}Q zg1__w6X;yg;BU$J?>T#VX9qW$*1rG>p#9@TD?O+$UApijQbgx&M8%LMPd$X+jZW*>Jf@`3c23Xx?EORSifoH1`n^<(flJvmxw2xh)Hwt!wTJ zS6yGC{gz(ePqB6k@m{f-1>i=Ys+KGW7j=?qbrnZ58bnXQ--nNl9#kGb*Pn`VRb*I( z0!_BzXARnKk|q7wxoe9lcS1inbx||};cp~N)TQB%!c>(Oh6M$SoyGJslJfDf1~?@T z_mx4W0L_mN{Wzuvnoa)}Oi!zu1ZF4Xzlj+>%&39O9Lw0&Ok6Y|?$kmcp!MjlXlS9?Us>QDk(#$sT0IGRI$S8LmYdaM+nkx&VOXsZV3TnT!w- zBetK?%h&ze+_((T(0k@SGiAQ`0Ck^r-qkhmY%jTGBCOP5hh-?yI(|h|Z(6dv)(lqp zSzFd^HGD9aoT^unS4vMQ{6s75h^-SH6SL#1hENp3j3`K4E2^elV69_W~8_2RN zwe4FZMrl!Fw0eFoQ%FyhjGbE-y7E~8RMgHp9^Hby8%fJ*kUhDz)J_bIVm$NXlW74g zp_*5kn7hLjOWp6PAdT1?bi^DCVa&F&IlNjBDF8b%3YN9b5>TO@6TY zdxHQ1pR!~U&m%Lcp&}*@xNvjA=2=;tx3!7ftc?j+{*BVHby^u-5AX(7z0$BJ_tIow z)m1GZ^YFllRNMth_kApZ9WG0kg%?}3{jRdag!kn&_Ilq|Eo9lZ zN_Z^I5e;6R1GK|m0)>tj@4qW_wrOA}S~HMqkmoa0A7eacm(9=yEdXx~xRaP=h_oqM zRYisud0PqFu~i6(CGnxEp%nRKS{}fA6Az)NgfW-FhQwUujf1r8+H^R2WyTAtlH^9q ziB(L=*Aa69ZS;7jFGv_N1$kpGJ`5Qfdj_gyNApiG<@5d*ckdn4Z7p@jkRm zFn^HL6aMT(j98-@zl6;KusKOgR*QoX^QDsulqV;Klc7a#EZ!c^1+$2;WgF)UzsW>h z+SO^XXk0H03eHoAKc$yH%S_aD94ZE3XA!;9G8O{QSRH!O@TUH1gU4Nq<^>ATd2Bhf zP5a1L{7S+}_k!rD#V0w_fhO(WR2Sos^>;RAzuDt}w*Y$4?Uvj31{3@;a7G-ZrX<4F zvQ}OXvbv2|us4*J8TB;jA($`fnf>9FM(%l-@!rQO_cHW4Y;FB<$K!dBN$;klB70;>@5MI;)K^aVj15#7V-zg_;(fbccrfU?%u5TY*G6c z;riN&lnJN72Mb<#MXT2G@i_aUMcDeTfybvP^@Ev&Y#S$o){l2DgMHt0I}ZZxfTQo0 zwkU!}eky|Ztlt8@BkL~`JWyK1j8;c@OBZox9PFl^u7C}N9oHBjk;s1Ql?kb4j~7cZ z@~p+A(lJR1<;6jvMjffpBBN5O8o$l!^x$T-FT(>Ss{C6N!KQP(`zHO^FI@5M60r}t z*|0|wR=_G3=@GaqU}H|N$fX1Y5Y zIH-k~ZhTdpQD2^m)=tQhd8&`F3;k83E@)kEY(710(_suTE$Fn4n|rHecg?!F=lP{1 z*<{eVY0h8ByQ8_LpM1y6jhbq5HF0qjK3?E5rG)74i*Bna`F66i{5T$ojJXbgfJHU- zqcVM~#Hk)&t8sm~9S&UsE_9vCj-%;VQLsXgP2WZ%l2qTv71C|soGvI$ufSaisW*nfCSVktpa z_xqCiGBaDI{aWssik5D2=4y&vi9IzQ{W@h#4?>DP9x7otT$a;zcy=q2@|IWY_BxN) zf(>fj@9q3WsB+wdyk8wFbJ8t7S*r^j8@B`Ql)omBhLs+R{`T&@37WV@Z4GZ&oBxmE z=Hch6)z2%dB@Wtlbt~TINT#y33r#?mB=DaEpjC$L8a3mA@7ttzm+E*)Q^Fz?P5InC zMM-(NM%a)3$zztb!d9xOR4^0(bO}Vd$w=M4}L@kQ?L8+&lE3e z>S$S+bMyPJ8D^%v;NVqKh%>&<+yGVGP%=jD!okayhUQ|uR6@RA7;TNS>q2LsLWgOj9G37!0kTWim|Ej3*-Ed zc4y|c=?SGBw)ng#cRT#PGUu6F+EQMOb(y8w9%GJ~s}Dn|*+yjvx>!%b+onp9QXA4R za0a5*_2V-en*DfUuR)%4}i0!t*%LzwZDqj8fmW3YcYJAk{rC365>)) zYMcMi4Y`d%w zspC21Js2l77Qsl1lQfiW8QiS6#s2M)Z2#vGugDRI2y5yK;@cvPzwz7QTZy+AJ$ zcVsEOznHn(QDXUp&9J`2lEhSnz##5n^SL0Kv8ux(16CHpx6)0$;$c&T>7h+DLiUcb zt6&VYVVmwO=jNvZ7Twm%*w8i2X@n3Ur}G}qA3TI2aK`s3R7*A^kl?{8v8za`j}j2)Cqm|s^p!XII4foPHD(r$C(njHv39)>T2Kom{)6TX35-Rr}nEgsK5WZuR$cVHicUu zMeAoA6)=U*MeAFqmDww;?oxR=S-8iGvYq<1oas?OI2an{s|`{474vt4+i z5?+G*U#5g_@e=MU0Mo)xJ$+t3iFxHjFzUaC>a&WrUo7l#y0~29wOaL7XD+0$ES)@0 zs6nmwt4tS<2Bu(M$9XMH!~TTm!b2a4thH?1%=j?z%)W1=6 zSFeE;`MPXcXr{4TrsbIfA9W%L;0D~RN~w66#Xh8t(#8X*b3v%*$aH-&6!b8^X!1t7 zl&z>)eZRI&aXI5Q2vyB28S-XUuDmU*U+rQ4I2}Yt#6}@1NT6(#X}zGm-?xZcjAn<< z!>`>?t{NK6er~Los1$VmqU%>N!mHfYIUu4Z> zo~9eAyYGgunjbLV8^^+>Oce)TT~D;`N1N!I&~l=kF&Cqyk7_|2PZ!O;G5ScY0Y&_& z6rV3;B;#p=I~ly_ab~N)qXpw+5dRps^g2)I46Ison)z}!F!QQj2~nesJB@zp2#MKv zr{i|bZB4lEOJ_#OON`sd_zRiPl4IeS!Czd7}?hN;OduW9$O?gk!}DZ=-bRa!@b!a6DL$kKEDGF;oQo?I@J`c8-H z`r60pwB^-*6F8OckvMjanKLdqV3cB7{N|?9@D+Z}rX#DiUwYD)@}~nLz!n#2*!;zE z`|l60MW2h@T~=;BrsP6Caobhl=jcQ3=IBEQ^!fi6vk!O2cY7IBV%?O-!0cChZpdtq z|H*(hUSS)R$3TR?Xsvlcc$jm%#YT-3;jfmg)q%H65AHdUIF1d*=5rrv3|M^EwrjOD z+teJ$RCIw9UYq&ie%XN6Ydg2sEPa;2T*b{yn;u+Tx1aS-aqqmi@oMD|G|mLKQ92!| zi(7h~=+GVGG$eq#BE8hA<1R0`c2cXY{1jQ(WaH!9(-c_#MP#>KVWdR}>`l*dT>rBI zVT4`DRl9gML~Q9Yv6q&Y=~+Zq4t!h|?wdi39@25Upio9HyM=45BNZGr1}@5EDvdwS znfj=?fl%xMHf)J;Y9&LXE79~OcVR-(`h3bLGHJzY68~|Tl9;RKM)g^}Z?ZO6;xT=+ z2I_LNX!UXGN9UCq)753+hD04t^AsTT5w@YUYLOZvmNbTQwx`)1N<6HS}CCBtT7R!9uSJ$kndZ$H~Fy_ z;rRKzc^-KA`*k1FVD5`~$Gz@~2hnm+4prileX4C?Ru9(m23)_!`c^V*(}&dmm;xc} zAg8Hig&C8u5RHC(?i=~L0~R748YxM$gu1q=7W9j@(2r?#gRrJxh@(T=owMS2MGMhh zVH8PD&ta+nb8>MaqQ+gg9cZ(cw~2FHc5>^!g^jA2EYIWbq_%%u-Cy7V|= z=kikE$+l%{3!{ag_UYE14EeSk45Fo^FUte$@GpVIq>SSFjnxs!(f1!aoG1Fn-6+IJ zASa4dTM2gF*qATd$m~8^JoypnMloHxo~bkn{HLVhz}CDzy>R`#?TxZOPgU0Ng8|f) zfEUz`u?_MCp&#c$dg)zCV?`xA;=az>cZogT^iU)wDxwfV)3b^;cA30?F-q!?fa(3K zMf1^R^cr8WEwEOMoNaSAeKvSeu?B*?R788mS}FoYzhBh;yZIRWcAr@g6b5jE+hFJ* z{TKZMy1!S2>Q_23V(W|yXz~&baH_{qYxuqoO0@bR00+_@x9aE2U zi_ZLIs&n_W+-E?EqD2PxL1d$-MW&-f{lnDJzgPgL8vTSDrKa7cJ?e>-rs_h6T7XFJ z>&cts(iT{P8wlYPDY;si9&02Ic(B|x%>5%Pry6W{mENjS3|b5g^f#NkE;zJ(C!!xt z_B`8Ne!LH<8?-pH2wlGC);`m+{(N}si8QUVrG5;yF)gL?0&luLYWg(cu4Zdx-O%U2 z2CsOOq0$CB{ATk}ZmEXB+P%M%++W2?ydr?Sq&be*q?_&0$M2#2!Snk(KdDU@O@Iz- zuQPK`Z@(UNs$<9h(-S^A%si(=Fh6H}jNlZ&(;A<-S_63tPlX{tSL*y7FwOYJf9BB> z^kQ$JE!@E*7KWy6Px%PSSKCcbDy@)n8$QF!ELn>mUxwzNS6UiiZWxA?A!jchAS~_S z2$qpd@VpSa@e*g!d^37IQ~CMI-Afy=Wv6W?ar$2P@%64%*(Wn{ z=UTc*omR=g@`lZq+8Sa_H{3T{G=xE{OF=6zk5f%V7Ka<7FgM*UuLV5s%tfRg7BF$W zrL?}(vWV9o~#Z;M`;d#ruOEc|r!FHz<1AM{ng z^i2-xLuT-M5OD~%>&pH9@OYjd=!#SzgxRCdCoSbxl2XWCUd@AVEbSgMNi0I)tCB53 z_=e+8UOwEn5@)I+5lSWH@l$fmLPW*;XbW5)ypZNDI#n^8rL(k;dNi&tcO)SR`Dv!t z6L^ZL0dj>X=kb-XC9Zw@gfnB3dyip>-jiq1;!B+mHSF*rkR9b;?$-Jxs8-1>jt+LC zj@;sxUnkUEjL)8`iPpWGzzrX2J(3onSxah=lU!a4^bW;MF?%U}U~kcoLbAY-hRmhZ zZqE@}b_Jr*nB+tClT^#DJZb}^c>2at=W`*sH_;x`YsN*CjxuS1jwB|-oQDT0TI&wP z0l`@Tq9dci{Hh(`sv`sy(nwco>{A^pHaHdkg05UJrI%|RoAK8@(nKn zfZcBCg{5ucQyoQtV|}1vmw|0J_jTxj}^BZ(-M>;lZ{dwOZ(3V!b18IlK z0rA<>{=)Z%(|T2xyNlQtj#WGHF}|#9epb1ChuT+K{>cDFDNNP4gESuRqLW6o(IU6w z`#p*G%77qOiJHJ4dL>CII|T0%oQ|ezWy0eSzBJo1I$NRSpZF?4e&v$=9}%$ykd4Qa z;gT1ju304%LLk&lIPR4SP;>1=J9eUe-zQw^zo(`4xND-PgtRM+)lN~ci?cnSlM$1p zr8dvbt;%r92LTS7!NVf>MN3}BV~jZjFc$k7;k9s@RLo@`W141HX3A@NFH+ z>3yE*msOI34@?Po&4ZD?i@D!Vs{lsi5MiQQ36}Ov_DP!|pd3oV#s3sP{d)=gYI6Vh z!Ay#!4_&Tpg|%CHG^Lez(}VKTsVX`XGh%1^Wa=OwQO$q0>i%7QImWQLng7)MUyh3f zbkP3+F4pWR3#!xFT8Zp!eD-^(b#KV>oYiPfoIqyhOWhBKK<_`((A4_-)jkjXF*^0_ z|6dqctA7`T{d{V(JQhfR>8-jBy|XI|XQx4FyEXUmmbs!!C_1o5=~beI26}w^SYmIA zcCpgptUTu4bRUSGT0(gpd%9J=rabR+v&kfyz#pJ2W;OM7a{)}Lq`gp&!SrFi^Sp)K zv=1gNSS!UL0X5t8B^U0LB|z*F+~Jue5wfO4{nq@fW{pq9TK0Pk^9oZVz?BIiJ3dtq zId6p+3nw$}TRmGJ7+RioOX}F!$NZ`o0KBjn?vbJ1*$o21Vld=RO{}1V=@hTy)pbnf zNEW`2n+|~qGLS#e7GC&RY2(U|vq*aVo1FDW6}eo0o98@taa5Calb{XZ?ra&d^~v0e z7UPeGP8%>uP!FA^bhmW=YTIM_f3sxOPdRvB-rX|>VpGtk_d>@eSNaK#?GgSY3cGyl zT(^UFq96rOYznEt-xt4S!b51|1jI&3lhxV;KEXGUbv3;*UQ4{7>;P`$d#I+Q@Z3j% zp_=`Zer%Vkh^Y_C+ z-sKX57p0cx>KSD0pNfN?)&931w)Cw25TlD>d`VY#w_^dC;5cS1vdIzL#AscYqaLKJHI}K!sh_M%43=Uj5afCkny%aR2`IUS=t{ ze0||IVlvETvRdhh*DJT{E^6F%xsR1sK}CB^)!*H@Js)GEpp7B0?d0VBqer01noqu^ z^5dfhjV|T_)ELh8Y}HLWB)c-%TT&Zp@&0|PO&h1XMV5F5x|MiqUM9$P=7rBgL1(;z zg8alC2MWnow3WS7CgvkYvhrH36`DLAFxvg*H(FNW_eJunjKAfFqaZQJJ8^tVVej2# zvn9!gpw=BsEh>ifq|-Bk&LWpF={^j@S@MGDQsk~Zsxyn*ggHckMF;k=dS>&t9Auv#e~UdvK}atef8cYiG))NPq3U>WtDi{{pdjeSajwkMvb+>QyTe{ec`SFrv%9O@$!U+i z`X5^=^}pBoUj^V%fIPBtgBWZ%G5$R80~f3PkGR;mjZPJQMYYt4byNJq%cmpWN^R3% z6gYi)IWYY((;kH{H&WHJrVP*QFm!x({c6vyo_m@Vk)I15JlLDZbXViE=oR(O2!}wd< zVFzo;x>;3eL$ zwPDWh4ch2+pR3GnYaiuPO~vou#GQJ(cgJ=n4KWTDA2B|b2kLwr|MHu8@i4|HnEGC} z-+#mGIzBs$feIyhxLNuayPq>qQ8pLd-3l6; zC_O8WSwMH%RlCgq3Oz5pTz64za-7WGMp0QZ_;!|1mpSW3z^0il}uradVKoe-#k2c zEnl$eZ2B~Y0KqM<=bNC9P+Ll5o3`4WB=;&-XJb zbhusDx}M>)Ve;nfh167c&(lxp_Qn;;?s>#=QSEraZc95ZJC66tl<`o=&KSl!<35AD zS9l)l^TJ)NQ0~@}k}9%fKDWF(HT{^pK~AB-UX!K@dmhj91|GKq9%mJ**~ErFpBMaHuH11!F5Pd@D+>6AE;R}6w0P=ZRO3X zf9h|SK8ugXUhwE1<~uKM*%&wUSZKIY<9@$zvu^YWbWM|6B zdEqwuC$g+mC6#*`x$=nN^dKk;$59>LB@G%U9X4xk!U#r0#Ik;khzussVY8p;uvzxM zV6!7pK^j^PcJb7vTWa%m=%KXhohGKbN8lbEi1tiA!xBOCKP-AamOU)T%fEW>kl(U7 zQk9tG-soh4gJZ7)u?OK6Uqu{Nm0rM_9I6EMca@+LWNiuH?xVIq7t`I9W2(aYVM6MtG;gg(2a;;k6KGRGIZ1-};s=XKhh4 zgQtv9kI($W+c?JM!o3o7P#dvwmM#CdjO&$pZP+?_J}7fpm01~g z>Ne3Hxu9x2`0`97_dgr~57Yy}DQI}&6Z`L@!i;*>Y=&D3Njfz4?K1=CBB_xjs{zS7 zZLH?9CN)}*syx)!Vj%%3##P1T<|F{R4TWwig^h=yv&O|;Y{Ovnn}FF7 zZqhS@{&J^FedX7}IsW0{X?(Q|7olQ1e1Bh$S)diU#I_r}LyWNk4-T~-o2o%tbDr5oi znwqK$t-j}*%DD&JPH((LO;NT>IY>PnE)`9&MkX^07jeznDx=EydONgjm=Yn~L4+C$ z&w}O1ukI&FSad)WacaEFgPrL{ogoN4hhX_W)unvu(^*jks-%d+UPepFpN!dBQe_`) zP-2j?Gg-;)*cC4k27Z~Cr3Sr%;m+2$HrR|W*-L%T6gK1;SB&~hVt{CDU*l`CQdjPG z3n<>I<3?wOj_15Y$49}#m(y(;dWmGW#!qJgmCs;)eC^+B`@hG5|6v_G172Kj5u;#I zB^NNW)QVL~jw^#(KvddsT(jueM|~`O#(Bxtb$69^DV|rzi=$YzIJ;RSCPFj;?aD^s zZ#eFfT8KlOit6ebC;JGodx-NS(iU!suH@oQcA3nl4-O|649;2!?#U4ABaU?5iC325 z-QSPL0zoMj2uimNuq5Y*-X@Rlb2S(9(72%malKoS+wDs$(1LJbM$GU1R+QGz2u2Ci z$tKV7$xXs+r%9Rcbqp~BxlpUe;d9|1mcV}q|0)=x&=WhxzBxAxT#at;^+2PDjQV|_ zE0NG?G@P=XPkCxe+;vrKYA#&-rxde(Q;I2JfwV7I9uK-zD1g}ZW$@zVC{_2jAYC9Y zg{LNzsk8CcD$jsI3SLV~dUTyesJSGsMl8oM%-s`%^5v9+F@`%lJ`hoctRM|+@ct_V zKx!NpLyH-6xuRFv{k$yskXuaC<>e8Ay z8e?GT@u-RO9^d3lu6Hy!&o2B{5L*9G5We`SAS^?2bj@nj>~-up5W6k@{wa`WlF=qyIG>Yt9(+AlTXNJ5D1(aEW$hX&efgi$GzvMgC@~wxdEs2pY ze(4Oj8YOvSFFUWWFsDJ;x+0qVOFyV#T;ek-?8bcY%-%n?sr$9yf{89 zFMTqCdaal%@Te6T#VSTAfyQ0s+zzev1rY#My^Waij$gRiDP7Q%oH?7$t2)bvk+Ql! zd|>D0r;QrJB7e;Bk@?uME?Q$61FRy6BHpAgTQRe!-R|V2yOhBp89HWmke}lxX4a~> zgeUD_cOW9WLmt#PHPB%2sr7qk<~leYazS!5JokT}9)70+9e9%aR~*$py5lngAkA|9 zAkE5cl4f}ljUTw+7@*OMi$rS&N`(=#@Zt*bF7i)jhXTf8%sk@@i)Mx#KNYl)FVEhP zGw3w})!Eud@tl|ItgNK6Ik?=bh>c7h9w?`0&@x&6Z3iyEZaYzZE$nUWhSQ<$05ls` zJ`WXCqwX7Gp%3%kTq=OkvUA1UTz-INp_`!DnZ$5Krx(6??;KE*fZ09o&k9*HyH4@Bka6FD;CX7Ov;(V!4Ki)e%cfipwT)k|C+>r6 zM9pM}EXX$-Fx@AJBePNq705FqPv;fN-13GmibgpS?;T(=y>G`u;_r9@8>hLo;At`n zIynJPyr0x~im)V}@l9J8xK2K&#-u*T9NVVK+~uQ6NV8B?vAjGqU+G6aebq`OM}hrX zm!SEJ^%8t83^pGGF*f2i5H1p;lV&+Peg1GAGL9_8!j)MD=NZaFmWnQEC;&wn4y z)76PLT8Y=|;C6P*Y&I@ck=y1uV5_%Fl9a+hqm%KVGQj-OhjufdK77(|zwEHEff zG3V+$`5{BN<(SM&#K1e&eA$Ke#2wJJrdAoF2*tOPO>*4&2fnAUNw zphBulRd6vXdj{MUElR0}TNoNHtZaYbj5jXi$a-j^lNwttSYDR zQ4_8Djsmt+$XB%cLtSO4iU#*Stefid;J6Ov=MLw+G;T3eVbhy?>P1Yv33cyy85rf2 zH8RP^o`=7XhpNiL7|ffDJDWS6b#=+@aDb79S{`)K5w(NjoBEq0!_4sfjH4um28A2d z!uoG6#a_efR}Qz=|HB9}>Gcd^Irs$SKq1XbkrrEG~|iRqiZj#F-agRKh7>QOkt$}s5W1FwdW=?!Hua@d%; zLLaS`78`mKuZ0a&c7MKp*10K-S*j*&oH44z?e$*KEp?-RT`Xp=#g7>{nAd^zqX-s>Qk|Kicm=A-mF9R38QMIatu5uvC2(|Z3)V9sI@|~s#Zeo)lg~Ka(-@ZnrUZ9 zV<?IUe8eAnosb8Z5!V5TXR@`#0zV}m?D|a%OOvvOL!Xy;C!v3UHJn3C#m>P1^40NG1 zLi4J6OV&r6ZZL|<)`k@@-ucj1^cSx-W>;9It=10-hUU09f`{#uxEeZ^S=h0*StJi} zZ~cMbIaAlQ&er0;aJBwb5gPKhhim0^)}t27A=Uakd%efAB#`%A(lDAZn*ip zfbJ@c3cnaAN` z^Meki2C^Rx*lal%wj7v0^N?hTV?qwM!1@>$tl7iz`_I2^+fsvlv+Ue^!c~51Lfj8t z5;`UgQOj9esOJj52jx{d#^eBTEsLrZVnR_YuwOKY9aB<1)W%jCs6>ykW?dwnB65W- z4{RaSw*Dm4E|_Pg+aDVxDDcWH)V|=@Gjxo6f;(1k%^StRrEzQQhvcG$y|^4W@%FO+ z7bjj6`(^SE$%Q5b8jeD7nAVnlD^!KmP7vzu99^K23F;Q``Usm6cyy0A+VR&uu(g7R2F4KPs`8W!?rd?k2K+=tsC5@_3q?o-Kz29 zYMDy&R(k)wt!Q|x5qPM+xZzibljEU%4-^$<;WaRYs41h6$~w2yJdHj<&57QMt47BK zP$ZQUb#2#L_qF0>_7V_z4@I2kGT?aF#3|@-V9d||drlFJduk}VcTIgq4?lKUZe&$t_fC=}6U8N67OGxiJ3>{P*%X<-5H&jR zDTxd9C^=TVq|Ew=bsw%wLg35ETiW3DXQaD^-`?}(%=_aw*f z<^S_SNyBhhdRtLF-Y9L2GH(3AK#5!UJJ|Ks!V3X+X6 z3HB}bZ25LgdTn;4oe5+gph;(n(CBGZiD#OQXFwsiMXKJu@4Lc=!M$cRHE(KYM#CMi z#qmONc4PV$Q!BO6^9xw*#MSm1ZCt_P;Z4x&D_+N$af6ve{yrsE+jev5iuJ?Y6sjjV zj9SOos^_)Vd@Kt!9@#x)?^I3(rJOm`;{W+h! z*>P*KwYY7c#W&4%Yr|BFWob&hW@t;NhzQYA>XGi*@t7UQqGgwg{PS_D)XDsu(wmLa z1Az&yC`Y>2E?)_?OYp9lFCo42Q1y!M*#j5k&+3lk%DPaH>OORB#Fa4C$AZ*xhFyEH zSOIlX952*$v7Uzn{KkT`A7!6xJ(OA zY>Ftb)fI(r+UmysWve?6{-SbZ4b&yhC4nACF05*d>vy}QD zTekYY38|gw^_bLd{wB9A*J#!d-)+#goHKl+kg52#Vy!f=*zIj^wxd7`y3}P2l*(H& zf*Jp?^wggT!}YBQgCSC|yT8e?tBbINiW;aDQ>mKOyu$hEp&>=g6BahxgfR;~+@eUH zkM0<@;PA0^FQIL$JNvqLSr4$@YkacdWbHozccU=_;>2iK@8F^>k|XR7&K|Q*e+t z=0i)QN_!C;&YM|nq`4Od!UcU(C?!j`-{5gQ>g3Z|git8ll1oMT?z7_j)-uZWr9*lJ zW1*sICkLoDHin?)?tc|bj!(?)IDa6}wy*m3A3LR}wFJYkeAztl9~97YW^Ausu=~N) zmFN`EfymD9u?g|ysw~Y-pWKyJKN(9Yl*dW-Vx^gUX_b*!vQY22Y(lU>=A0Z!SU;o= zFoBM2J?#G_bS|IpqaTQclFukCKEM9d?6Wv%-9?q!JX&_11*-rr#Nx>2t;EF9>BH@6 zWlOtfDxneh@c#i*#Fhcs7Zf1~ey=IdiShMxJ-VVSd0S zm%B)qZj_awn7JLdLKGHT-m&TbgPoO|z|A?Bn7I)EZgRps0dSM2C_U8S zhz0hogNgZUMAx(NSsR;j&Gs6-IHfNJ{GzFonU8z*7-9vA;P{`s&Gp$^-c~o*^u720 z!=wAtAH62!ncSA%V+*8zlv&RheOYz$aHr#(~GOjz-MUu^Cr*)4R`N z`vej-@W9%xx!Onw&cSDx2$}Jn7ofu}drO!eh6t*g#R|YFdwikFq)HV!-0~qEZpr%> z+_LtlkZ(v|mm)o11OnSCgVdZ-+?yMig^ZTBrs8?7vb0^NbW@H?^8f7#D8`ZINsol* zWOY9O`taaCzNgpbOThsWP~u~yVXOcgXNp~jOk&e3;YCUG&b<>#J;7yxU{IZ7{7as9xy_qcCI~p&3zbd)1l<-ZFQAR z|M2b?Tb^>uTHnkNj!CmXlr1i}5&7RlHX&@U;0*#z`_o-Nt~D1oJb0uKsoQLFQ`*3_ z`?1W2$2V#j8V=J2zM{6I29@Kd*9ssxXfp*=AkBwE<(m|34GdMV?9l9um zW*GS=lQ7_Pga#9%qLif@u+?=V&p^0@U!m3aFlo=IN*!Mozwgasz_0S~k~T*lj$i=G zs$WZ&<@ED?RdK8!~X&p2Sy2roTa^Qa^**iz)i}_aj^W z^`aX6{Tweu$!AKl(L?P6-o}YPxP1t4C<<%OVj#k^1nU02_{homzFE@6qy7nswX~5O zi2OV;(^g^AX}3W|OI=D;1-0@Z+W*%1>SGQJEe{&Eh4O%6_FA}6fAgIy2$_lM{0PgJ z?eYij$!ptBk_m3NC&E|*M1|r|sB>W~(W0j3Oz$7vG}_Ih8|^Mk*z=}~#%-KSOG!Gu zzH|;y`>zVlb{_Gr&9;uO;AL5Xydr?i811qP>;;1&GCVE0lW@cgFY%T4?tMkT{4kcl-uS-(n{lmx&~|_=e~cY;Vl-AJj8`F?!MIs zVcBwX{iLZZO5Vg%uDr}h)ik`MXNA82ukv>^6-^x<}(|XhMFV7FN$JD^yH(d?>g{>Cf8k@Cx6=Ux{E<_ zLLA#qX{jRFF>5nXD*|R=Hm^^Pv($ih^Za4BxaJ8D#7|G61 zQ4XNZgSw3UnmlIheZP63l7~p~z1E@n48eLmj`Y9ZIenS~FXY}l?NIY-@W}MJdf02K zJ39K}?mqd)HT_YCn5DdoT2xZFnSSlJ^3#0uS5!!h(E}<%N5xGHS!7YFOBjnVh|X1T zWWtQqSVp2C%1=FpwY+-jlwVjtp`TJ@H;o{M1pY~i6LMZ4PqACNh}QxXGF7w(WUeyT zs4z#gC8KYTU5VR_4e8LZiv5I+V&7?|T5WBf@3&Nsv4?kHOMzYQCxIL-TK=YH1Cx{ z7P^x>4p_beq%KH|W0t_sz&i&hJ{wLlN}i;9_rmT9Lrnc%3~nE?(@spNepfknWzG)m z>e=nZ0ofDApndFcw@-D5_5(?_905z`5ucRZs>Nz=9Xv)V2DGHv^tucQ{?|K_u!^2( zp{3sp`uiHOeI{SR`$1cVT6}dBF{O+M^?bzl_ThitI}gg8_Rdu3+3VsB8cfHIBEDR3 zDyH8?-Jz?)7v7`#Wg-}(R@UV%V1F$Xb-oXoV#gHAb95#VCdP$a3;D06`bfEQyybJ! zki-?&4Q3xVRt`~@MLtywpUZMBAI>h`xyut{6|U@50J3-`1vAn6)mhzvxh&%|uMb>= zsI@Ox*`ro#zcrhR`LbJzx{|4#Yu_W~K~oiebDQ24+3k-6yc>p*#%w zboABEALuK-Vk+xfxpwH1CLMiMOGjT#`M$Rm2<-dz!`GJ``1&r^qfn$P0#1wZGA7u$)pv^=M-caoFWi%Z{U#F2(3 zkMvY}l(i%jFHSWcxaZO$iqjP68`Zns!s|80mp2?5a%O6Mb>!h&UL78qnIyX<+IkDl zNvy@sR@4<}HC&|Wp1-dVmPCmdU9sr=!`;KJw<5YkCSyma!X46IhPT^4pfR?+g2ei& zPcT%`$OOjFQsDVpLHq*qq;PD-IfaR@ihImx5Tc*w@+V8_q@Cu0$jk2r#BoO&!NCX` zN6og7!1#W2Wo}T|R3zMvR3At(S>m;-&unj9;W@fsyX7`jEQINVI!SXC;>~TUEI917@v3b4rhtse zO>NrLq6(%BtNNClRcTSrDsWR@y4bUC?$zVXH9p|LM6Yiuhbn=RMsx0kl&xn%;pPon zydYba5jQbplCaEV%}fxV?oLoKb4@O70aEM_sBm6W=aDBg?3FZMnG))3p$uyHOGI&G zfJJO2?fOY6sN;x3%dIteEQ-iedj}f%mzQxlKks18wX+KLds|P}iJWNG5a+GnalGg1 zav#ECQa)J6_<_eRCb{1fZu7)f5cu37UettS%0s2sAH0*H3c-A`2HU1}q>(oY8c5}j zb4p!z|2He+va6#Csi5N_?O^uq+`}8&So&r|3L{tgH`W`zkrG_`3^HzJJzrg_flC8S z<;v*eSpPQ4f+x!DYedpYtx>HT4 zYbskp)P~c~*H&G=XgC)J~T=O1Y# z0*FYu)f%-!hEDHDT3V89c=@>_}JtZ;Ef%>ro1lJ{4#K9`y*FBUVGys7>@6V8T zmQYxiKG6M|J>q!#_7FbwE9k`~zxT-HCQy@kIYg0cVJCaOHwfP6bCjAO)nsJKaaL*2 zDg#56T6fs2$xF&3b`!dDBidG%f@9AeyE>;4kqQ+-TG$9XtbOTbXMf1@W}|%ko#YNl zMLYC;;-^AtYiI&%U9nM$ny}KBl`#KYo-v=rpsH|A(N~uf5Cw_uiv>8`$n%hm$YRVW z;u~XBvK(DuoW7GIPe9P6e{$odpP{J*_qyb<_!*Vy9%MtS7F2I)kGS=R#idu&jyS`< z%6>HvT%a%Mbi7NpH6+(dYAn`vi!)o}k#7{0ZyTIr%UNP>7!)CE7$#P}zF zIZ+>P7HK{`5SY<)3q54`mE9OV-D$WnN<1%rb?242m{8yH+}mN_ASI7Q-)S)%^b|ur zL#V5SvBxcyD`Z!DomY<%9d0r%UH5Zm?5$+31b~Y7&6vjn1%6w zkIuR4zVGjS{@4Ha|NXAtb)Bor={l}+&1c@9_iKGVo~>zaZadtRb#;e0_Kr%h2W!kq zGx8Q^OiV~spqp^wNB4Eh-7ot>%k$JV`L+}X-DouD1dqbK0EDL8u&mqt$}ju0I^k6? zZ_WsFsaNaFn*2E_MWpS_Cu!ej>I49wWNQSwZq73ex!1bRLUDE`t3lP4U7tO+#oYS5 zIARI)h{H5+ zzblpu#o7cBPdEnzdQkfhV){!N@60fT(hK+ES5S-WK;9CYxnJ8PGbeS%wnB6D9=!yn z1_#Q$Jrr!&69Y5%a@>h^A4iZp`)(sOSv$$x3kS;&hMy?@m#U^!5;PD4K?6~0^fErc zhjLDOalEgiMC(y`S#ZhwF3FHFrAdZ3dJolBc52e3z6On$?MhsK#3W?yX^H~$WYU4G zCTot8*8MLFnK4(+$vK*xo=e|am93FiCMi--7Vt)}VdwIPf#a(%rOikD_dsexgKx7O zu{p{?c1JIhA%@|^ESIw$?d3Ix1OJ36Ew~`Z=F`*<>P-MkZKWWit1=N5L(x89pNx$C zg{4Hl1QKqLnu$W;DG`I6U(!w{mZ|I)aRd%--<_ghg$#3^^CMr4Cs(bmn*))SU z6RlOhXbSiF>UPHX)25s=>+aS49Ia-a5w7O2z0}HLbjlCL87%IiW?gOB$4BRUjN7Vl z<#+hfL!350}=2&RW|6o2(YK5(DNt3VW(}c>x?=bl}&OI}6<{ zfyMzn*(JW(yhUIGva&P~&E1(SYez7H#~s@8&l)}YPPspPCFT#$r>;Koc7wM?6z1{H z)zPvPOGFUMrQX@R5$Zb?Vn12XsUD2}@_6sXdXFN?<$iAR;*(uW6zb1Ca1eA(i04=b zQOY~l#T+U_L~~C^`$OhkY`S_`UWOJnuM3Qk<($wC?Wvue)?+V10*=5UQ#>9X2p2=` z5;-k!?LEnULUBWX*e9dvjUNeJxw6VRRon+wuu6povulpq27OAwhU0T5z^u7hItXK+ zUFW>4lf1Ny6bV;gfTad2sO4WoA`?ja=thbQSuoBKWZkPlD|&Iw&~2f93qY00pNb|e zH+j_qq$ld#D7wqYC4RCn$O&NAxqe=r1RjUp5CL+xX3`99F>27wPlvFwd&J%fk!cXf zoeW}Yl+5SFlBPKH`stRHO2cpGCWs_b}{AGQdW|S=S-Q1aPl7A{#%ZEi=}fVh7$_O9a$4;S+ zYv`Q`seiPRJ&`+_oHX?bB*!L-%no~Bc#&z6enH>OQH7CF5{s#6r5ZhgnxA)ts97N< zQf*jwRPkzaG-UUi@mXBpaG~=M*Q2k_W#wBu^BE(hpz>}JWMU~iNq+>1FUd~CRMe)B zuqG!~JLPBOL$_#`=VcB)DWH3?7gn%Y^_LSUUhPI;SSp$f)NS^X>c1q6N9}I?Xle#F z;lWl>REbaA;(W8f;jYk4EoBB(8kz$I%2K#yq^E>3visA6dy70;XLEC2h%@XUVhzyf z`O{%qcECg4^G_mEk1A^2*az?%SIGGHrdSH6>X}uE{`|55d}zlXV2Qg`r!iH}uJ?^=Hk&7w32~BNj z#bC;p5x;k(X2)rCm&(vyBqw<>lup=H3g?O{zC8@Gx zlWleZ-w`cY@dh~0O6=@LX>~OKPPMPRCHYI*o0BIYGdV{!fio&R2UySbgM)_SWA||z zDH-mBk%^!Wg-&&UFw!{MxMi<8Igi^TTk!s_0aeP1tt0y5waN`^rES|AV67i_JIpvt zqiwAfZ4mdf3bwB7)lFpe)pxR&80I7ePm9P8b<4WyUg%z{kBI=_jBEgfN9uWHz=!O7 z+->F2!InqOw~pwN485L`S_a5gUi+tmM=L7bMTgk(h9Kn`5%UT*il;-QZ7qnmhRyYf zD>X1Gd%CQpi4W&vGrgK8{r2LzdFP#}@ujcYRygLQeKm@U$i|`agd94LB zr4X)N_gUlVuxjta11!fPI8P!_G{GRx0IWe8{>K{RTOleSQC({5igNltvqv{^C>|jd6OD?8z93? zP2tZzOil3m_)Z=@e_#YBZS%Em^2N#YF>WiOJdfU%{8cP9^E@}Sw&jEX-UI2PfGj_B zZZt}n*d=GM`b;H$j?j2y{-H|=+qtQa@%=hypp!PkzSm+22pL+#k4z16m#EJWlSdD? z3dYTnUx)6!NZyY}H)Ygs)pA+j4ARxT@NU6AHm&!laNi+9zjoY2MlAi zgUDM)m|k7@^pz%H$Q7%%fC$QK+-N(qlsDH5^ZRyE=rG3_vId-oGdgP;z)*&)*ZJ5H zbIS)me?NsWz!+9g?0aMtIryZKK2!fqZfV)aO+m&spbY{4(AfVtexF7HKeGj+v;QHC z1C6t4dQ#w5c!yhv4e^iolE5eky}t$}jZ^Fn3UZD-S&YNV%Y^De42(2bhmFcTd0=B* z{2WwNwFUO45oJbdRO;K*Y#iV&{*ghUw1HeD1B|U zWY5M_CGkqrwg&+nkZaiTa&-auD*5W~)G3yow1r1?9{0t30g*XhV49(ebZcv#dU1F8 z8?HaG3w@2J{Iywpa3i6J6IUZ*sQ}Z<`b-S50jm)kfWz+5a%6Jbti?Ku8R|+5e%eMN z@y62Zhzep^Z#)+G7zj0lDIS_JKQ~9SId8hmdk4-BvZwp1mtMXB-Lhq3`O-LGL0eHz zdXMFF#v{0vEqly*eL9<0qT9?gR4^-;b(?h&?9*MCpg9t`;oe@#+6@?m!1}!_7kPmc zW>RMwlo`dC7=@=VzUSQ6`1m$>FiJT{_Hw(x{k5!~RP$2ClcZWzIt0`^&+~qf6c5-M z*?nlii05?my^goNU<&-W`V%H(vi*5;DdsvLQj-&dioX^y%nS zQ-3b4*?;DfU~Wae|4Kw-ROQD55BjlC{yZ68oGe< zJw53%5mUdiU>yhaNz0t%*UQYYu&ZY@xTNo5&J>^Y$FQm5w9@6tmDY@hf^mw!+5e2K zBqd>E?oubn?!9L}1B5i|tKkDnOxZh1=l4t`_0Sshq$+a%=rZq4Y)Uk^>Dot{cC0Ws zolmqot@HcNVXwp4b!PGOY*H%y-STYTC_H~6ui+x>(#j1KU3W8ye8nv;jJn)B<)N-i z@pnYa4RVdWRmCwgkGv_9l*q#cqxs=M;v73h19pp?1JVxKxewBwm_* zfx;2eJL1}#VPZOCk>!Y`RNP}Xkbx#cy{;Gxqec8uQZz^td5mW*A*n7KrG&u;vuG8K z)+zU{vGI!+5=Xea+h6L`dA32SFrl?udZmCEFcSWn6yR)7sk#%0O(d57=WYyGC;Hq$ zAOFaT5c8v}O)&@wem9{lR=*dgyNC;RgXQUwSrzdzuq{2?oNO`QN2Qx6x(LmL^%&3! z;7p*@DrF{}-mZI-?4lBB;oe>;Ze{=^A?uL-=-S?9qy_$ciG1w?W=y`>MAuu$ca!Dy z4*XvU9K1v&gM*jD0>GM?N$4kAh&I4`!*B57A-UDY$BpRHtM#|`CXKW3; zuyVF>g?p3T3;^>>p(0aA9m=gXj`0&%Ln;)OsdgpUBYk>h_JIiCKJnNXRJP7o1>Z3{H<5=b`XCH`)Cg?dSZ@lOY-i4uO@|aM z&6v2TGaquaVf=pTv>xk0-Ya5^bHI0J4N8&0^^!^bMK47^YxW2}1g1703h8=PI+7*g z6Ltvmku&Z+W}hN|)qF;F9+Gor9(Abl#B^XM=5fJzr>pG^9(&}i2Bzr`in%*+v26N1 zGN0f%D2C;`sOT#o)8|Qu!)c>Mlr%-#RgOI4YkznbN__ReJ#`W`ma|K3?=&O4N?fi9 z65rEP-H=SQ>}Dv*Ju&OVi+x-$#<*no*g>afHnj53+d&~UBa85j{7?<@zSSYR%&M@b zG0Qf-)5p!GFTZ48?`&TUj8#zvBjp?&9T4T!PW1JbIGSrvdol>RxEBsW z4F-ZlfXH#8zcuk)xSc4GH#;`}`3;Hi9g=K25Tm7%+oiO~OXOTPx!MiMb=>_ZRxd}R z+~AOl<4paXuqKf;I2*YV;ghVG$I?*2{K(wf2QDO*54oUG+vQwH?JgF_f%vTS-(i1Y zXVAW=0%pcX<)z-w0u5UOgc<}6vkBL?4`{S}_XSOiNsSqdf3@sLkV?uGeov6m>_<%F zzFxj$ZgYumEf=$)%8JVHa_*V`PRVKL?O%vZo@Q*ynNN(bD5YuR_W&is*-<8;B_!KA zuzQO_ieQ|xfHdl#rzhvi4o%Qk6|rDle_y4~6Ho8<`uy`fgUNm42KS%Nf^PToBWkv}@5!-^be2 z17wOE`ALaJB##l#2!#X}n{!qsKRB|}rOVz1(d$j%OReCj_ES2AnWVck=!$&{ql&~W zQO{Q3P3_w7;}QxLRRhi7LfF(k>S6XDjD&^&I;-{r@1t8>LVWa6iDjhu!b(lnN2U0O z0Cay9gtwIcT5o<8xNo#$=YUZubePbBbs3FK<_4kx3?wZ2V)eS4*cORR$D;{fq#PBU zLz`O|RZNsu6UQQ9UG`$W^8yv(K#wnY!ON5Ju#>ZZ6^G7C1l~HA zQRAuZ%jg9S8zRj`1uQ{7h%dgJj{6IE2%j=%pXKi(HvHMi62t_U<~u6bxsuk5aaB^R zO+hjY+JB0dZJFcd%;u0O!sr6);nT??DlwhL9J!BQMyV9V?`W8dPoZb$n|Ay5W;H}} zo*b2P&k3>gt8YK(T>tnWh-?kYitJij0PdK7A-ek9Mfva4Qp}%+W2?F~-;}O0?%tQS zWX>>1z6{dySLRg^duvi|IMkTky6s%*0|}TeDZwwK-gQRv`U2@$4T1F8B9YgLAaffL zNng$xieIcDt#GXx0GjxM3>=WA7s*H6c^AEJ_b+*gJQguzRZz^$i7U%cHQoNhjI|jp zFBI&aWwx|xuxdu1B&D#bf`Sdd03%>M+&ix(%hGG-fup847*@&(y-!)`%%2lEtv#!*g}M&M-(va9LDe%j^lK6G z>5a){mn@6KGt|J;JWnm$VYNV7y>&19@l&5sfd-JvT!b&=;xxA01O1?b$sgUabB)pS8pye#mAr$=A}Py)&?4H?R|>9 zG)g+`edj$>_JfexBc|R*g>XG~5LMM>X=ddkynmrEtX>GpDraOMd3o}SO)KgNUJDPh zk;9rV~=y_>fv zLpCfd!elE|hZ{jMllMO+GlzuK%zrlnTX=N8V!*(xRT7Ilww>A60E3pJ4FYdXjn#g( zLFjFt?CN1X^rt_q)iB8G(ScN=pv+JI`dCN}x$ULOyF6`)ZSU%TmSzUgd|{!Hf`)!jjP zyeuUUW%L$79jC|`kR2(<80K)-qPg#Vk!RIo~|H7mA}gyAC48v}wiWxnTLE4b?oTGhm-EKQ7_ zZlY^l0|_o2b?5{H&Prlth08q#BkjczRU|v`8GJHyX}hEk?rial z4rvy$fOdZb1SOy*HZd_o$%duex1cv%q}rQqE`b$_bFup4843t8l;0Z}2<66~)untmjs83m9}gONRR=K@ zmlpRhpLdqNbT4D-B!##NsUh^}nckm@%s!fA-dkP>^62(%=uTqY#bI7r2Xv(9^PZu6 zHA*IlK{cypL)7K|y^)(r1d6}iQ>`DwLhRa6zeFVf&Jzr6g|B1*j>I&&rKCQPjz{u1 zous%$(u$F)e20*@zhk$1e$$4qzG>L-GO!-=+)o?~#}5azRA3*MjNf2>t#$b#o^ysg zdhI&#uj-IJm$G6FXI0=H>$P-cO#qjfc&qDP)7OdWW4pLhHiei*$9mu%ZX*txcr?Op z2{L7KRSXH-4Lga7-wX966jvKw{Hfi+@?*#%H9{Kk?ns{ZROv%%)j{V;?FQb;;gZI{ znJTRL+jzmP=VV@N%85Guuaqq3Rq+6!WBMMy6t|^>KCWN5`kRdKO?Y{U|1DNfJU*El z5a3(eE$l4?@XHYU&KenJqwojjY+58?CQW4S8C3!1gdFP)?1}>F(r`rJ5_gnOSp_|_ zRmy5FWb~++r=uzizN6y!UHL;QqB_o=a-AmUa`F#vx<9zdcZpL6AgkMZu*SAdf)3bP z6I*UBpRzi-U!~f-FyGZH=pfD~$5jQwDO^{`Lk))YTX(VHo5#b1!q1klAC9o`gH3$S zh3ubuCrcxGP5dA-VQJN_^rRE)9t>h$z7FPwO9f&GA9;Zn=^CG@mY}-L%JXl1$r!}R z_pqaxHK41Z^H%ydkeYo67>vn9`-ZhFsg0oX48jc?OboKmlOW0NYC^Zjb31TT>_K#u_lo?SxDeH z0%-U(h#+QCy(vuH%rB#iXiCcEWVnl|`B3{s)4iKiVO8iUu(5>440WBSbN@_f&kj<2 zDX;mSCHqQKXN0C#?7H3X(oeAP3Hyh*|X`o!A*2_&QY?-8ZpEvDAhm%4UtE3t{nUkvYtRh>zKf5~0*81Sh(QXLppPS-m z^4=GiL;Lr$_R}ZBt6lzTU=M*jM+BTL2IX|awDhJ;xtgV)I@dJ?&qO}zaR5E)0e-^s zAInjzLN-G*pgk|BO%G5A38OqHpiUZw^i3;QqBL-uZkj{(x|x_{R_^F&m28=M@2=6$ zX?Kt`F|FmWPWH?wYcM~^xsj@@pmGK!fjxrZ zI9jPGM&bcxmX0yn8%Qj~#1~vVX^HvK)}ZktQ|!PB1B|)$k88%Xi|4o(53UxMjQo%8}T}5m4rojWjYi%eK^r{iU?$j8+`F@3-z> zkdQaYESaK#$M=+f9!~Z7bY%pH%^7l`r-oppIM1K4R|ylrrUR} z--@b#&^C~nfjrOmC!U(z#rJOPzELSwax@NtW;r&T`K~nvwR;xb(z+Ef_5!nC>uTd3 zbDIF??hhf9RvSM`M_J74mB~YRd`i5FSHUGh9%r`TcWbx?T)CqLuKtqc^zin>M@C=v zR0)20w|HZJZI4XPwWHSoQ*OlG&(z7AXGCEXAL!6Mhf=z!~Vpm$v^YL63>xp{jRy>^_v#vH%F{r19}tD zLZNpz3@+wfB2B0pFP- z-%q9EAih_Hil=o`4m5AtV;KK{_1%-B+u@^MyrUBxNL2{Q)wSmt4O)Y;E$1v?U_Ky|?C428QDp|4y zh1ig~k5jCH-g=f%yDmN?DV2B4ZNqgdA%L+V_RK`^6`2jFONGiwUh*wtHlO+f~!cf8xi+Sn~c zLOHOzyGio2LIWW+2<3VMgJE7;x@i2=e*L-#ex~Fc#@TYhs*@BO0R{$7IelKFP9LP> zAsKI+N5vAmpryBjbmnt$sgN3rIDSb(wbzq-qbnA`K>Mrtt>53D82+={3h*&oLKo(! z4A+4xP4sW@UD9!E{SX_>QvpqHbvHLMqH6%ds(*x1*%?}6a2jvf30~{G-dIQ zufl)*$uQO7KZ!Ne-`1c3VOf|LI=XToa2sdn^HU(+K{zj-xOd4gQ>Gr_rnkZQO5t#j+EVJ){I;W)j%)`kQj%RC}sqHP-| z##OiO=^aj@YxM4GWSV>Fli4tV)RFIkh?AI#?gXGdsBvbCF5xQjvF&4f-=wn||w z0d2eV#WIDE^pBn|O02h92#jFUeJJybYfbgXEskGui*$euc8(&a5Pp;VzOA2lwS%23 z0)NK~KesG4U-yS){@A0t;pJ5lGstAGJ;1+{a+!5(1MPeGxFV6YYI*Us-(RJ|hQJ9 ztP}Qwguf0QQM{e}$4_e!%@gYzvsRi1OY;#8(iU-@qi+TsZ!DT31-_vXZtCL}VA|gW zd(jLFnlPw7c-^s|<^U)uxMONOQC_=;Bl!NhrtLPlO=L*hAl*-ZDoar04_i-q4g?+3W1=bc7^w~fWfQCQmc#$4s9p83=m znYjT) zJlJN@r)zO`#e0kgSA^cbF4RzeDChUB4<*JV{mYjZff;Wg^;lS~SSR&dNN}B5!*gx7 zpoLje-?Hh%DHRM$dS2B$TMfaPHLFSd1D~sAeVEjkY*^Ae1i{Qd@W_Z4_~>Ot$!DrBaz6%**XpYCVjNA?Cy5Zx?u>_Qd+`D-8JYfm_O(knZ```T`9 zN?N-71NXzi$Ly|10b9BDfHL4{t3SODCie4S1U=__V#Q{1i%a3a-DPp6uTA+-@I2iT zdE~(C4`W|;sgAGZIGqA^P?k=QbbtCA{`CI>j%owRJ7(gf;V;|^B38Cd zCHH(D^YJ1~#xi;!EAF?3%FZRMiwhb&-SY6Ceo)8kj_@6_EUhc!$yl)R+xdOH-K}Pf z#Dowur81pM|TCdg7-m` z9G&^h&E}Npx$$cB`P#!|p|J{ouZgw4iH~T}!7O4418kaJW0R&|8`4+%ZWL z_b7OFffW?|Yf%=keO?>4i@oObF3JCH?Gm$d=Bv@do}j5}l5Ow&Faw#TJ`F=HryS{@ z_YGW}0A6!`kkQ|N`Tg%l_-3F>;a~DNnTl{JKYCQtmS~T(^==*<$)7S~Nm_83a4XU( zK?>2isxwv;B#)12+=xvgD5DleJ1<&4D#@zplVMJL2EMDk;LKSYa5T*uKR@S?^NFMF zm@~?CHLC9QVIM6!hp$dmZgYined43o&5QtwJvtz(Zrgckc2=l3<~Xd-*EB>}0d@Ub zW#@VSL7g{tCIAJ9=Uktfm>@Bg$s$nN2JM1?tMq(nx)rW9MM57K{5mwstoeC2{o-~6 z{?hNP-84a~;dJuHnQVn=K^!EQcF()}-d*nA;h+IgsVfJ?)THid)%`qS;6;=1v$2RU2nvL>_PTQ*g?K^B{6qoZz#PBQal9z(9_jZn0RwVDk)(qc%j(dCJ zu#^%JD37lxM=1m4@oPhQJO3HK-?GnT4H2`_`wtsGH;Z-3{ja4-J%DtTm)UzN+yIj3hm9Y)V+)H`APF3<6 z<6zm`!G>BJ!voQWLceUh9n*@{{`B16?P;42D2*M&jp=eGXbvQ^<%%5d9@EmXGewWP zU@+{`e2t<%NsAuOYOPKXAr&UO1&hS-!ozlN-ma{t_X;~(E?-4}M`uCd8;DUshg3Ay zea8uD&VH(*1|<2SjOS^$pe&^+%r)(=Kxgc#dGu+}Ze#L9lXbSCNstb@59=da(BWFN z%52}X>C)8>m%*d=#clE&M(Rxu$YOx~(ZRR7e=*Su|KT+Er{CNgpb&&m^CyavRz6N9 zQ(Ap8Jk`DQnT#M`sRTRbp&a)+8oR81@kl@W_{iMaxDlWG!J!dBzEhI~7eA+|Z3>HX znI7u7-z$BzeR6d$18#F7vj(Ed%qs{oOSvmaYnxk3sGe*q0n%a@;t^EA~_Nj;heTHWGR+RDL3nE2XV;#b~wfHOflX2!pounY~jl1K1HJ5rQ90__7 zZ+l)d*_0e6O+OIdauOkc^P6oX;1-D~A!~(!#8?!yBr(OXRAv?4B)iVBADtMl?Iyd# zG!X^qFSH_C%esJI33;7y*aY{1dr((7WcWH)!=h3sR~+@H#+c3ryQ_HyHin`FhX;=k zx0CPOteSeGCmeW7VBo+y1s%ns_HF&usYWtc8ZVm9t&~G^w-Q;~$uGtAjgh}6`R0zH zIjCpY#47fkFGF;T^dyRY`w4kN702E^LzY?KT>}nc8^_H=J2Y~JRl)_Nez`7x{jlKY zqoNarepmHak|mG1O_Vq81#4G09*mK=-l;te=6x1KR+C%3vnts%q+;(CB{8*&|LXI3 z10wM>zd?Ou3Dsm?$NIR{L<(5MGeNe9O3TGINLGaToG8w)yJr7KUKWhi-| zpKY?NW~oF}tI`H;G|G0{Ww`LQ$8O?{QHUm}DAqn(LGGCPK;D~*G<56m@f^#xE8_*0 zkCw<4SiRh(wL3`*`hWpn#yESP&wz)ei?egiCt&pgJNa(0zT=NbS;+V#F@=Nq@OEk; zd~RIY!qny&0D$k<{9mtTB~1$7itl4tYof}}Sic)r-ltdcc&-^f*In@G5|i)_9*hQe z&h#Pywb-B3FFZ9xs4V*asbuN>ID60wddGLEb@o9C0-*;C_f$lE zd-NOY?w+F(g(s>K;tliPAnw4|dRu0g2BB_sJ=QS^+C9n&rAp~q?7(4S03e0 z^`r_m$xWh(Zh-#|WejG-TK@whbhe zpPW~C(1vbzmF}grcCyG=y;;!^gX~r@wVm7WPs3tR=ctOpwF1g#Tv z8e6CFDcW^MH-%JdGG6e-xkz>1r}lWxp_J`iGv60g;`Qhs8r&-Hxh;OP>~l-Nm6Bh; z!RFc6e2w|aLW+XFDkKL9;tU*-4wasXn=6TvUfwZP|UuMFJdd@28C zXmD1ke;Ot2)grJs!+PP^{8qDwBmjj@Pd7Dn(HuR+-20{zGMdhJl=ZKh;N(Jt70~OV zwB@_w4~$8C^M8wnrV$s^B(P&G)^OT!lT2oBV%LPFoqLO^>D-X+Riz65&(deJLk`ob z$)2T5qiOiz?K__GgwoQJk0ss!AE`0Ii=GBmhJ5R5)t~IH4egIVp6GKJf@(P_PxzwL z(+<*+cYf}4`f2g;E(@)aX6g2IaTmXqUpRIA*D(5TKfh4`R*Yv#%%2PW;==bz667AU z{ESTElR^nndk~&n;y=xNmgX3Pgy9>S%?;g#=KVZOzs-8Bh0?Ei4NdGif>k&mk`}la znWU$;1t^RZCs`M;HHT4MQlT0jb+F94ZYQ9(mQjHI?lqt2G_)m@Pk#^BgUAiLLTuxc z-nt0x36=o*&XcgmtNPC4tNKoh-t)hwCl%|;@$v5uf8gIIWDP$S?soGSelvR<;ck%W zanjzj)GR4`6c@X^`Ls|&DexY=zRhCa-IKo-H~;!``3L0(>*8D&|ABh(^D=#UXboz# zaqno-a`)$g@~@oC_u#|>N0_gCQ6#tBS%sBq@&Q)#y;2*YRaLfpja@vgqL^SPI9BVW zIH&b3?5)wrGZh7hOL+bQeNNIQ4(w37`>5Rniv4k4EXL z^I|tT`}^7XG~48dCcx-1T-$Fh1PBso^WvJtXsMoIE+VrJU0x`R!>*HI85?(#TYbtFi6^#9d!hv94o|qnw4oQu+Gp+w~fU z1S!P(?EqLwMm|#;-(`a1-c5WroYbeU!QF zeND=Jb#3Xlp~`N-iM^e6iNvv{nrlS;NeXwaU~7=5`{4t)fOps!PBhsZ3oYawb~C@I zLRLxE^XXPi(?=D@#c0dqDv?!;6#~&s5#ryG5Z9U+7b~Yt_R7|E70LoS9en-YJ%WgH ztc=Dj)By)AW1#>0!wS}<5H-7I2OjAE;(`7zdJ1m}LryHuw(pE*_WCmxSL7vpt565F zd7;VwWVbIIS)Lr%BI)F)of7)nGVQ-!`G3`7U&`-*Ztjp_nW2D=Z_d@JBtTqkEXd!j zsDKKW8O?OPFxyXE>X8k|M5ZS_O<9YY5S=9#xs)#_NRFg}~>T=r@HVZsZ zmfw%x>dG=pBot&AQ4uvR+m*WgKg&->>Kzhs*B#$j(XxBf^?ce)ltAQVBb|w1rDTEz zgj#5|J|Sne#?W5jxCyZn+w*C~BDlw)nHIwSR$APMs6 z8b-ixHaPWjNdO0OWzf?W8oLF2OJjv!H)CY-`i z>{Db(nJ`()1gHptC#1`>pWfGYInr1opTum!C6)GMnqNwarn#UUdX^?_``j|*O!XEj zDx(d(ZG`2%X-2zmJAmgXP3p@WvmgF}Wv>z`u`7^m7`y+qlS6Ln)X(Y<&N>TAi`hN( z1IvDTPUE4ku`seKx1$IZY?0x(Grd5@rBel=^t;mEMm-gN)N5IMe({&6=OiJ+ThlP8 zy~qH;{uoJMksA%q1_F%?l`%;m&~R)@Y_%0c{eCGORn?L#IH$Q|@;QMydGm`Ia-vIu z7R5|TGpV^|%H5lZ^-^Ui&&x|-F{-pro@nlGwRQ5jwky|oO85i3Q?qw}d#)xtNLyPS zf9mk|Kw5N{HFrn%#f>l2OK4&|Z}&@3Akav+DILwZ8J;K_@U|i<#)97A%9m)|)xf<3 z_Kz(bqJgnu^J^Iwv^z1Fv7PKR`Gdmls};FCGuXCgjt;t^!@z;~_}@4X+x19-Z~jBj zYrS(ngX5WPp@SMZ1{qsfm(Z6xurPl`zLa1QBmnrgH_WD`%}X27HOk=vfR%CxqDRPA z*=mSdu66XfZ7ATOc*_Gl?uu{APA}&Se@^L(CcEt9iwW9roW%RBGQrR2L;cKrTc7WY zTw@RhxK*(+oT!iqXKRPS*_}#Qk=>946B$DRH(HeMx6GEKALwCX)P)i68%H*l%X=(e zp9;5F3I}6?n>S*_Q*_e#T7p4qBuaC~@Uh0ajYaSSS!FSwYe>HX;N)0SDunP6bY*U| zDxJQZUHV2{#ROaik1Dk-f1YyC!~55Nrk;aF^VIY62EYD&}uaXj0ttVyno1TY~dl|*H8BEKM#0rzhPpT9~p$L0GbBBsXLe;NExb&r@u z2UsX)_HwG{xj;K@g1$A8kLmwGV?WvgXzcdQX%c^}cs~*gplqBS`oq^KY_|hSG(jaL zVX|{5?1liOZSOdAvZAh{a&X2?4whLFr6>_CN^Jk_K_Kc8Nca6PV>3r_FHEo>0w-t= zJ4xJ(Hh^Q7{U?rnDCDjiDA}6_;QWvPUJTQOMvWYIZAas72h=}||NSZadL#y>lyCG- z7Co`@q%};1od#0~m-yOW^2O`Ik)(3(WJ(9rUJ}C4Qwj##(GdOmjQz)8T=O*k(QUJ6uxmWIsJG!_0s!fsS&FO~w*N zojg<4=v;4&AK;CD7^SmiBnXOqPLPksjxdg6ewVHRJB|fTjKsEbvz>4hZZJh+gzL~4#W897lYdA2Cz5z zISF`Y{KD0akT;bCy4)O(>=~$<$JcA5=bz3K;)@Wsx&BM7dM}lwS8ePa-G)pOtf=zj z>CGL8oQo+I0Nu}bv3G}8$(r$6X?$XP0wA{Io?fbM!cfOIHueLL?=DuvI97D=<4;TrpKQNx@?I`7TkC?f{qN~eLM8woAJC%b&%Ed<2OT9B(C@cR>0knDWi!srmgX{;-|T{rrbE$mIX?5t zK&TgHlN%}AL247b@w7TQ3T5m#pARkPWY(QEHJ&Hq+dN}q6$SabtB_o>pDB9rN_v^~ z{3^5ESnmZ`YYiu?GTT9qseA9b<*=A&^FEn`9Vv3CLtuGTO=RxDG5h^{9NN=__8k+4 zXsj~ZX|X=~4#@E)M*}zh^6F8O{R@@y6*NoEP?Nm@uR759x+M~`)Y#JJ-|U5^0cg8+ zPP;~q+B5LdwCg7|ChFz87e2GPkZ|DNug_oKq}_4l3VwaI;E=$SPPGAd51B{5M4~ss z{2-&2oluqsCPgL+J3sP}WCMR~)YNu-sR?*p~xwfdLFnj%Plg4~nDckh(JKL~5e zRnnooX`HNPN^bB~bnth#t-vE2yT=6w?;q546Fjh^t54dEo~Enei+NLXcoC4?MFXq! zhX;?=hV{#Mj4|J}HDe11IW2D?T&$1-npXW1MYKtpJ}+lsS_a!@(RG|ywIFDg4TFe;c#zH!^ER(`266 zPxW^vyBr5EFkIl91g*<5!{q}pB=d(8ecJSG;6z^$nZhlPjD?FZ9Dqww%)2-J0O;O` z?vo=rZvEp-c(ER&rKfUzs}Y@U{|;bjB_bN56z!|7c0TfHv^O~StmNuWTuxnTY~XM! zlJtIj;nUcC`&ShafN~p>5avJU=;FnC-Z=;MMz2DmxHZzP@eesmYtz=zyP4f-plot| zv@CV3z|@Qn8p#{EZ1rTnj#m*y%jIvHZxU-9ciu1|OfK{SZ=Ajh{3|6%|K=a-;sDz1 z%+J>;=bhBGZ5DAIWt_DD`5sUj<~Qt_%*BLr1Ls+@kXM<{P{!YcH= zHQkhNSI^y2d)ERAJ@KbqeIIC;4DsGk$8(F8rG2{B>-9kn(7>x?Om|cv>64r3w=_W~ zCgN=VDc#yAQ6afC0!j;&lA7>i_OU5MpPml8SGz-^$u~XDV1nhsAUCI;5oYhawij*{ zx!luuLX7U1e>KTBXm0HRhqQId4(OI62B;P{^x}2(Z!<$G;R~5aJH3%9&YII%(*M1o z3+|Y`M%zKBmUhWG?dLPl^aTBhi3`mzZlm(`mm^F z01rvK$>Io#&qpcyajSb-_Golt@Oc&&-MF&r`s${uCcGrF3b z_9Y?h>>VHe`)&H;PW|!c(+&KFT8JLTsmd!+0fVQ&^=-Rw)W_L?y^N z?wWovXQ5N{q(_z9pk?N3-P_!dIa$Y++iuH~a$lf^db1|-?_4|+EHgdCCk%dY-&>wb zp4a_w#RcpcRkg0}8QoD(_)0UL2A1{PF;D#;@b`>7~OE9QvfUBdW>M`RJXJFx`qXnPN{+wj=2fX0fxzQ*senn=1!sg7G~ie2gQ zCf0V=(@M!Q6e{O(rkDDntJre&q32F#j-s1&taB`NN2`0aLqsKd^n%JEt;nZi28YuE z)&lPP_L--Dao@FyE}oLp7l!)KMm1gp%%&;oPj*=pIc(BjAXi(0fNn%3t2AE$bJ)&I zed=7fN=J&sN7}aezx~QZLnA>7zD?#?T z%o0;>J=Sj3l4ap4vZ30Oq8}_gfZiVpzj(y;inm??MoI7u$;4~M4MlV zmp0V?Ex0ZVkyenS6y1I;Xq&a;+{3?H@DTNde3FM9%RCE}zkmP>9y-aF`Rg5M0?Sbv zIM&;<=Ct^Z^)zBmkhnK%$M}ba?>&;MD;_uhZN)?DbZhdMG(2At)qkN(u8Pgst@5c4eSpFG1oG4x z`CpbSsN_~?5=Vb>IRZG=A9}StQ*v3hTZ>4W+cd^npUM{5uYarV-V;^&p}&m`N?$gg z6gqucdG6;m@wA_xbLSN&d%J=sFc^}QEMPne*3ZmaY!EoQ{^awEr71PW2EORKx-Ln+ zfzvGJ>#d(o$~&OFEg^D&bDgf8tDJUN5$?7HsJtoCs0bOw&oUgbr*qqBPO;)JhqKF; zMZ~1GoaVB(a@Tg&`M;*T80b4pD9~~kef`dH|4w6a*9WZ~$2O}g6Qlkg^4>G73AF3? z1yQh2ZGe=Bs92Dq0@4x@1sxT}3P=fMFe*(5JtPqU0Vz>YiZm4kkuF_IXcAfo2!!4V zy#_)cB;njR^NhTE&-1+JoKO3D_qPFLE^^m(uk~NQf9d$WpP=@<4N&`RTeD`4t1bIT zRvadMfuO6EA)rPsH3Ubn@9idT-$A!;!`>8?v?@@ZuLSsl1|B$IA%K08ayZXzEfeWd;HoAEp&YEWcFO%1|xYPI|b7RvT~{}-|K89|9>rdP|;3JGPwsSEwe=K1lp>J zqS5p|o!zz|Yz|&IZCM%dvdGg?z3I_Mh}xqc=1&#q*Md_!qYF(?q06?6 z0)eU1ph#f9!B|fh75UQeg;e3QU*g*zATJqv2>Tz`{`S<3>Wqu*>0Q6xT@!lmR=3ns zJ@1zO3#qPt+ASn>&4a8Q(Xll64S3Z@!{&2WecUB|UwWoNY?wmRjiMpBV`VYH<2^r_ z>Kdc=G&22K>YF@eHTrp+4@xRjmDcO;(i2ZhYo6VJsz>ICNy=7!8{Ik78-vtQ){~GF zpcJQ(#QT@!pZas}9`L=9Bym>9c_-3RkX~_5!XxrlkZJIO16(4F@onG;D>R|E6QK7UTdeq} zcO4Yqp>E6CA1v*vLX>Js;Nrk3j%O}x-xB2kpNjks52o|mb3pj_<429g zrV~)bTeS!Tz_77~@3hwjtw2S$wx_^<%+WL{_Az8m0$;+1Y;m;79JULQ_vnHiL(>Cg1E1td05spAc2gzpd&eX|r&NgfXl161^f39l%I`I92~g z&FN~uNHy}4kf}0(h83|lqR=XK4ot;fmXQs4Sc2j3E zFy55bjga8je+~&=ZG;5WniHGGj z_LGh7-@{r3N`!b-)eatMRl%v2ljcynjr$UC{}Zi8-oWXH?rA-rZ>8+OVoGJDZFkY6-e09&(y2no)B-C zdgca*5d}=e2nWg`jsm6I=|`M(EKSs9?n?2Gc|g z|8wc~{PrbqM_h%xko}d)H9xQ+mQyX3bo?K!QxM#O-q;g7hwC>5MAchIL4GC%yBwuU zmNwgv?-ctd9k1HpYtQ`2*KQEqH@Yl5Fg(mEDHQGYYDn`ASwkapRlj^|?|xzmTlRjJ zK;p=HCcSo=TjGMa0F7qV+8(!UNRSm(Ply>0$i+nOiPqc|JkweG5W}pN1^JYf54CLq zgr1JBkbKN|FAvYftu0vdrPf;kd-Q;_QQ^EDddX@;D@Og^pMdRG8-Q(#Sc!Q1-OnWqYFk6&kFJ(ryz7zl~uOPpAPgoUV)TY3cCRcDn(y% z%FcA`?1r4};NJiE{Dp$L>;%^<>rHtwt1bVxC=<4O|KrC8((wfu-@EMzPHtEvx;Q^6 z9(c-2`WAWks(Uc@&RhKvfF*kZhW+g2bZo1)hr7d_Y?2DO6wS_$xahAezM5AGZ)UYU91ALe#k~coLNR?dJD#f| z)Ll+zIE{4gUu0r}CtD*EeMCwquf`f{j!O+u9inw4`7 zB+^* zqI&Rst0VJuh-uU1o>-hjKhkOSPD-7+K7Kl`yX_ZB<3`&~g#6@!#^utN(;prhgi2;v_!fFZu4P|b7F2i(QGd#>h<8QpNJWH_ zNsN^a`6s4|!ZwXhB5|q!3kp&9$Tb2TCZa?Vqbg^$yQHA~u1bv4w2!|iYUJB?@I1>cGqtpsM`P{R!D+4UFg zBI_SlF7CX(|LNn!EyMMP#RWByI=1QPm($7HevdF+^OHp&sU?> zyhA0giH8_$qCe z?A8B9{fkScSP zXbt#{!MHy8boU5-_!Bh8ifH8nNhc?SzO3gc<0YYw1I5v$G2X>!Z81wS)%T{=2EhtN z(@2+jvpPN}L1?PN35qMH+X;Vp&Rc~T_Mf8%N{G>fSRM5RRTaZmKxyupq%uyylY`tK zG*e|1@AIIYN33+Fo-*)c|DPDe9RQ=a1A-hHIIw|Hq=&+zD+^0U%`evYMTQC6qb={D z#DZ|xP!G58pE1Jso@Tr9)DS`}SYC)({CwQ-hJRe-DY2 z!oHspksR**(HGBgmvAZ@J&q^Eyj1s`Wz(!f`Nb?_Y;3o?t(+}CXou(Yv}~w6V9`rf zN$fYkv(HT?SjDqIDU+6B?8fi8e?Ffdlo~qNZ&zL=!T4isYo^QsAQ3rY2hIvbYj)bWPdC-R|vI1&$fp4dNwZY8r+~F7@WPT&Ii zPmz@ntqMHfMJ)XHufGaYu&HpkzhajKT5 zn8kAj{;`blUWnS*ND|bQnv2S)PlqmyXfJ^*j;G{kT$rjW@ z;l}nx>1)7QArv(H+n276I#nmZ?v#2qt4u2X`wL9Lackci)nCu#>y?@%HyJO7-Cz6E zZzXzD`d0gplYb8zO2!*}ukN=ee+nuF^dz&cB(NUs^qi%DOC2hSbq@WLi6*$gL~9-; ztN7E3F46?1TV=?M>YUZjqyy0cj@4n<9FIG09^Lo%xvWT!zK4E@Yd&)ipDRYV%<}$T z!6oxxODAu|sE1+xW7W&3GV*Ls1Ti2o#cuYzf2_}*B%I=%wzk}fZ6%5wg6*Xb78Mx6 zI?5|e=Pv|wAWtelx@s7k}ykM+($ zOKr2h@2A)~plwtw7lf?)idQLl-S#KpYV;mzVRq^7xC8(GZ4f90Qe%OG=k|RC^|2Gh zpQbk@laD?to7dmI02-iZMy8NivO~H6eY&Rz$n{|y(ynX#%(|U?k}J!E%;l@cl*?a` zibpI%&LPg~z}7fZNpE{b{aUli3hg=o3L65b%Ww>D;Ov z`6n&Rw;9mFnqVC@?%zYQj#CR2br+H6%bgSij_)NuJ*18aZum9R zLdIlDy<6#GuT!r>M*q9H+X~Eya{Uu)(?$9Kpj1--)w;fpuN|jB0{qH8mT&9xeJAS- zNgL9^(p0i`IXAX@%jMmHaAmB_PR>27}@6vD(r3OC;N&%u8n_t)?yqsrd>bd~L z5c4wfY!*xSPFu?R_=Qnj2RUZ1@K5&EUv+$O+fcqNMHOp%m2}+inTvvzR*O3^EX&5b z0^8xQ3ypRK2$heP8X7}NJhIl#XjT`(Sb>+Cq{&hO0v9Bgsvl@)=*PN3Q;B|P9drjw zTyW})%Kq5Jjm8p?w3u|ZG0(a(tg0({gFP>V8(>mi#WO#~IRD_OPot&Cwp{63saAO{ z0Z7m zgfF{cjV0&Hcw8CuxW^Y(Cq6}_jUn|nT&4_O`|YAn=Pe|+z>MSy=2PfP;WuHw5`vpv z0g+|Bon>Dq6OHTM;LYwV_n?cu5@p%@Moh0_E{vy(+NJ7(=DG0O%uT;Cx#t1+*>leW z>_0~&@97Y;0H<8)$Qk`W#vBQgVDYDB+&eSL$6pls*!62DqaNH!%^EEk&~!iQ>=89=EcfN?c^F_FI-x({s_gEe=hfQMK6B_K~R@7V}}$dnQ&dzgsYAm$KH*b zuaXG|0MlZo(<;?zC{X52zZsS*@u8F*SG|PWyn*#cpl-q{ktY_`*x!XC)>mq<;oS~! z*!|eCM3pHA^FrEP_HP+sr}9TK=$8vOm<&aIEC=aEwS5N+0g!*V&b7N4pSJg2+Z-^g zzHqYi?_ZTt$lY<4Pqu#Y3UdpC)UIml!yEuB^}ObCEuL0 zl!G3~Ko){QP*jgVKUxsf)vIEx9Qz9xHH8Vxhbn`%SJA@A-1QH7>HvVqY9PJ!1ht~g zA{f*IIESjA42lTQ=6XA z%NX^>W7x%?*Bmw9{FSGQ`^M$rVY7H^o$S)=$>oPq1SbFLxpv8>j-Lz9d^{U4$F$ z8duQaf1ZR5!GsyTC>08tqKjPE?_<l%rOmep)58WGh=f%cFtoU476OEBKa7{Pt9TI z3)VrW@{SS2(bo!R^V=QfWz}a2v{$3?9*&)M^xk?9J()E!ddBKh$Kvv}*01*}T;|$^ z@Fc=YzD+yg*IWo|fFXYiN_~m5pZ~n3G0z5mt;_Q2)0tLurLdodpc~Gx{p;m8a!@B0 zkFM8KpKG0vFC0mtOzv}IyG;pT)(2@R=az21acaKQe=NVR8s5uS58fT@r2&~YFcdYc z$QPE3!mRV?O^%8^G9Q!)kYs(i$ z{qV~<%-m2MYzl^%w)(bilj%tdp>+4JN`7fvf5OFotlI&PHb8uTzK=2mU-z6#ITL$} zy=h(i3JxO$w}iEoIT;X78k4bHE7YW!JIh^GYwDUH8kyZoiVOzX%suJn{QuU%l)XOs zze>O?3d%nIcM>r2)!WSE?&^2i^I84nBIFJu=@Dz96(8O7j;YyF>+kbN^4Y=Uif0Np z6J{Ph4k7B^PfhdQ=x#0lew{zgqF1X{3#x2mCa{g;fRdqfhbBlrY|m0gKfcUrXbJ6b zzvq~iMx#@PaHgMI7yJfEBoT{Bc=TGutZL0*(B~GpBpcnO5yeoAY9k0BQ ziRXAznsLlZCwJDBBO}km?2oN8NxB&ZL#0T!<0OwpWZhH0UG3TLSK96vyt$>P`pKm5 z_4=CTVSUu(drFxP|K$F^uf4ZqvHX5aH}x5Sb=ve5lqv@mv&XSUq$Ic^nH!)5*^n(QU7} z7Lb+!sTXV3X){-%J8lFPPomcZu*K~h^Gcqbu(Kwo8u>koN)Y3oF@m}OxFU_APWtZY z>s8v*%!~XHC__F2&7UqDGn;A~ysEv9W_wVO2(+W7@!o$tnSAziba02SuQoq-BC8m@ z_E|UA^Jrz$OIn_`o8S|R*IQr^8p3CLPsiv!ua>ecet|DRs~0cfk4>-2*9rii;4hNl&RvS>i?IF?lXSPJj5I^8`Z>j_}|Isj%sjb9)&&S9}iH< zl1{A+RB(d_Ym{cH&8#Zw_Uyux-hT8x9|}kl|9~n&RkwS^aZ1FOL}HshXh;0akSFEw zf|i9Z%9+KpW*uc;S4X4YAF7DDOq{<@?CYy^g{hP`RcA* z8xZpLG%Fc@E?%e>t0%ygo2dzq^_*KX1(oPJ#dW7^?XR7BUE1GRg{t0dr&T9utPXq~ zAArp)^)stg>S~i>zQyU|KiEy4V}->Z6UMB5z(fd55j2;ZDN)#kR6VzePq33RG4rMN z&`v(tvlYyltMGfSeI8?dm=(rO-iLQPi!npO;c#Y!@V9v6fgEqYjMRh3vBnvl16H#I z!^;dagqscfT_d9LTtduxzwoki)IZ9Gf^lWlpk>o}p^ehI3)^;bDU~pDPjaov6gA~~ zVX+QnvskB;!lbvR<+AHb7!GVUdjb`q>DF)m(Sj=lP*gY?I3~|#@^YM#`nBEwS&SD8 z{vZz7G>|py$!OVAHXZ}$50iyc8*&p<_kc zoCr@cT@0dy5N2cU&BTars0_EkB)7qt<0|HjK9OOnYSRHOONd^Et6rtr(&#PN=*z>y zZ(MzzB8NxFjRCO{iJ?mV=ERz}g|0dmFIQMs2{#Ly?)sYR7Fy{#l~742QTygk4kh9b zlUqni?3H8VG%Z4xaFC>9)wA*8dquPbErMq^qag!`ZJZo{)*O@#N@*4zw3PL%hIsrt zWAx;be)ujq=9fgqrSSG}Q#T2Q@CgGm59?JX@FRh&|MB?>g%h7|;6atrKfZy68^hYM zc6=EQMIq;4s(dXYQWO2BJqFFYim|BTSbUoybw~CnL;UJ(50fPLSdk#x^oYy>Bz7 z#Z9qhQ_J0}ILxM2lV8ESdGXfSFW!`Bn8^(&hJ9!1jq1=s(HmWe0`;n_TU+sIs~}=s z{UnLo&USIPbbpAl(w(b`VXe~kwu9P*qKb|tO3^KL z88qKW3FClL5}B1)N=uznr9oOQQx|qdQ{UN>l?tQQGIhGR@*dYvq?%gc~=Q(4o!;@_P0Io@IZbTOBM<8a=Ui(0pvjq${k(zojBYYwac zTDgDWh3j^4os8-446@adjIUm4{@F~~zhX*RV7O&9m~@9dbq4WT9!s$~9rJ4V9r&OY z>$9Kaj|=o5;%9r{YGW}}I3li%D3DCtr++W!bM*RcS?VK5#c;**K%2mM4A#`duCO?-`KX9wYNFCpPEN5E?+e~V7Rj5qe1E9o05!| zIk3tzQGDRxFeq?}gkmoiotgjsCJZ|7ZDw@^l}TVXCw$AOw9V^m!oKFQGhbaL$)8$Z zc9^rf<+f;-7iEfnf^?43M2*|Xt}hG|;i zc2Jl=^7oWz43%rcZ^%pgal4?f|s-y;dSohGeX@rReCh7BFSG7P&lEeP=u&+yW& z^p6>|lw_3FWLo*YN2gk4$Bk8nK=0$)ak+3)=BHQL|q!C7?% zb?a+Y6D8kRVG;OZOfrS74SzI&Xp76WE0rSbkSQ)2ZX|K_cDCD?%|9J`u%uSGZ{W>nvZXLC(ulb_)&(c}&qqOnvHH+DbA zeuFkfBR?1>`oN7zN~K&^u@zc-cD#5QBmSe(-JE~O1X4{FfNX(;wTrf9Dv>GuB(Q1)iM zAe%e<=Y^h`1Wjv*R7_xws^E`9*iHU*KdjFZucJlvx$1-9O9qtkwYh$+bL+*6b)xaB zww;(YJDXb#V!L2AE&WT12hyxUJ<%m3DV3f9;R2n4wEPFrYVHyEZ(mmTI@o=T%4)mYE9~mpXC0AV?z7c~ zoC-|U27QI*yfzjG4UU^xK98(5?qh}nw;Fg!pX%-Tec^cb{nulx-+=~{LEvu4x#z!LDx01!e7g%kaw9jt&rFZaw5Ajp4v;qex?wz3POx%oYJp=>|+>$%=5d+Hyki#ROq zR0YY81D)m0AW8@}{;(<1II(*}jrw60{Sb{&@p=|LHC_UhW^rireLph>?OVez_6a)o z^h!4@XcyEYLI%34jgp9C`umfm5ytUK%G5(-Ed*}@s+^>@a(c`$RQ@Ty1#a|y70$$N zeVdK@L9&HC>#hUZdBmv~4q1!*ejNww@t$Y^jb7C~1<;9b*8e0 zbI#inXxmyvCm}1+d@J&6rv6UwnwxA@PW1NMV)MXql__8M-N1h3s_cx;(GMPeUJuP5CWku2afdG~ zTy@^GlaFjcPG)GtU>l=?rT`rSJQJ2>9t)6$zv-zDLi^M)D2b)P(74e(e(ubW!Sw!m zz1jJ!1nEBU(WBLjkvpvkXSiXg13i1#WY@FYV#p=33Xf{DXhIxQW3m^orb8l_pkiGW zgIf{@;nwTT;EP+rG2`iaa~z>s+b8 z7hPEjwse(9T?mVCUYuAayaQ^A*?g1-?S`&P$bM!p$p*gmExmadC%ITdO+`TZDwh_o z%FhsWqk$=VN@^Oc*(b(hG>Re|0q;brGdoGLI7|b?Wh#$z_;Z5Uol#=6fjxML0#^qX zvXJG<{>>pK=ek4^_Ny}9%VRhi-nA2?jV|y-C#M(9esgM2B;;A^#!kg^hu>JZDEbd^ zUi4w_5Ib{=MX~NG@Cxj2kLSCWDF)G#j0dG-FJ{*`U=*L-oNu1EZay1hTIYd?U!C@> zOPNI8La)H%&vCPDW&7(=3ISys}RXarU3I_c?m0^t;9LyvCy^t`@E8W0(n4wg7XjY{0eOuAQo% zv6?hH7F~J^t1~W5L-@LOdJw3L1l%kJ8W1nT048*&6PeLBx$4ivFXT`QU>L9b^?&7x z+!jF$NVHy_wtJ-A)J6SbAmNfU77cU2rN7*s1g^~EUqpUAwtj8SutN&r-20W;MbQpv zaLdACFYLrP+%l?=GP-f)X3KWCFo`Ey0lj?hQ611StSBCKWK%@81w>@~+3jr5F$^n4 zX6Ru#OE`lA=rhX_O~uW9V@1^w1&8*(Kt;tmM(i3R_9iVTHr=%upauqfvL`yHG&h9&+_@EirC-h zuCnSDD?9T+j$6Nis(4OaN=(5*)gcQx;PvsL?j z$ORk!=BAr5$!#~2X5JZ!pL7wKvwr-$`T3+rCxyMAN>j+9=eOs!xb76WTyXjFlNXNz z6<%!JKKO&>B}1|p)BiCN6Z3Jb%8lp+)DQJVWXyWaG1Mv=H;*0hGJWRhzHNjzv$%0C z?~$l^=UwhF2zSMKk?mX-2YG$HUsjeuf!DQ4zdP|t?(ip?#gp$S_h%Q1`^V(^qBv&< z1IwQ^et8$BEvpFa>klG!4ZSz!g3#A2b4A*E+RpZ!W1O$CXavm_C((m$u1`d$?qz2} z+YAx=YqW!PBOr=dFKt-+_r$vxEl3Ro_9G+);-A>2tZ4gumlBMg>MqfHU8)?))d?Cb z4kyyfI+BxK$f$yD8B3w=Y7s)6&*!3z1`Cl6*2@~uV4+1PND0USST}g&dv=)u?9NlI z*wSRbo7D!aG+<6S6l?K>5_|@FEAd*?P2BCe$+ndG@356IP61u#TIp$=H-DzMdsz;6 z^2xYqL&a$eKola3UwXr*hX#h-E)JOb|*$sz!=9QE`T{NhYdT^2#oU^!MTmkEH!R66{eO~T44(L6fGE*eR7Z2I)0 z*3YxoyoLaRne9=OmD7_CYlJ7e>`5T zAh>CQ{`E!WEAMjN%*TSjfQgE7%5A(ro=kWavIz4%-Ko_LoA#HG|LagHk;>Njq;=}i;8Un~2Yd=zf? zB+ERD%d)bu`P$B->?2b>IBYR@ey7?h^iJpxDafL@;XJS&b3(e|SylvBN3WsLid}NYVDREU ze2P_dNhgr7mGZ`AXLJH_bu88(&fST^t!S%vj?HCFp6-lgjR|P{-pCsD5c~dngvz)1 zBH4Qdnb+bK6V^U9%`A>ZK+G@{odx65(*c~}B=l^y^^cENOi$+bGQym`zcFxVC0|*D zn()V~X7~32KT5ZSmZbPT(jv3l{!|3q@eWqD4qWLs^;kjruieC*f5&Tb$+B)=-0|lQ z+%#Ff-UOW|EawzyI3A z3JSYLA`3`RN}?E6BV(Cyc>`^Ra+#epjk+>mR*Ig?+cT6Y&1jQgJSJ`Q%=GV-q_akb zkw-biR)zvdzwv$`#*k&g9jBu%JwTClPkkh2B@w^h+GU{>UQ+VXKc~ba5lRwdJuJ8L z-s4kwe)mQ9V*)kaOwK?8ZU8aoeWbI*u|sIuPSa*Gfm?C3d6)8V13^HqJ05LuCiL5M zd8U3=mh^IUoW))z;+gigzIkJaX;FtAYjQS2Ae7eky9>9W>vR3>jx}(dhdhR}W|`(( zJ6$`iBWBNN70qSob|$ea$@<(vRbsc~ik5y*VQxLSTA|S`dTUds?Yg?Z%>ciMI#ahk zBgxO&CpO+kVM*8I@XP6LZ-BRlL~J}s$#la^ygW`zBuh_|Hf+sU67^I0*3^=cQF^bG zO38S=4+pcTxtpPG`Gn#-JVq{$NhpdNr-3;;u?w@DO*LWh?siyJT~fg&h(9j#rcff_ zvwf`>%+<>(N?EseukEXkh*+D$XK2uSzEBexfhlg1UXSxn=bXkcSM1AVxMUUf{qtO( z7U>A*43}`M0H(nb^}s4tP0nZUroBJdc%CHV>&~^yjarQME99K*>Q=`t_ta-oMQ*j} zbpY{pw{gHFgzJ+#@H=aB>~gJ|OP~GiC6=ocgv*{w+&~=Rmcrdj9W{#AP)Hq);am+U z765DtsW~BQxj4$PAodC^P`*4YpSt&4g!IQ^OVY&3vC*i5pv3U9QCDz;gU)Q_j|q^Lqs`xlZ)0=?RJKPWW+p*@ z(n!Ls2s5MHz(F(n)bi&7SpeyrDRy$d@<%stNtVUiz2y98C%BUvx3%fEL*a^vkVuaq_alWr1LS))CCuhY&St2ih~3iB8_G;zL?6g`@9}KwPq2j2UMFqF&fSkwpdnck&`)mJjjbd+yTha39h17~@ z^A}S<+hl^R#M_;)4a$=Mrp>O?kyIJ1*G+n@G!*-BUMKv>Rml?(>+{b~BS!}*pN+EB z+7r1AAB)pUrRyB<^nlZjLshdgXF5a}M#U@TPP99)$-WE7J)Ap?;1O+Z}~v%;iU1l`s0Ba(M0U zDc%Tp{{F&TkQdyUf*(VgUIY8DCWxG!|9|;mDwbd$CeA$Qh4hhO)H?xEBs+1ot%j8guz#s9^_Dg=`je zrYteu%a;5VqvWE@B+KaAvbr+qlU2OGadh7mO0bL6MwaE>zzPnDSG-e3@#8L^?@uTB zL&T}gtc3u(q&M?dlowXYkC05(76{4djk@Bi9s8C?qy`wnfjdgELIH|oXg~xHYF>Qg zgLGBkL%wOU!$O&m&%>v+tH5aBQeIVA`JBe*YjGN%roT<4yNHZ-I?;RSugBo-j#nQ_ z_u(EAMTvWKEL<9Ed*-=fbvGYFSO7hrS(*B?Zjwx$|` zB&cRWOT;Nuju)u9HBGEaZywrT<1Es*G05)gQ0-1rV666vXzsUZkloKT@n*^<$5MAl z&9)Z?jX7n^J9qVUMU>JMV1=e-1E4z1gBg#R*! zjz-#x?3BxTES>yr%z~i_@3I+Q|TEA1F<5&+0JlkLwU?PhUy4D|WZ+ z$e;ykCzYzLZl^WZy5yFkfNu|4F_lM|BsRH7?j(2(49UcMe);keEUjlxy`^#P`l`S_ zS%23llY;qmukHq2ebquH7VwV*j2a82tB2{54!4C^uI>^SCkD|8;ZG zLPp24_*4|7jjA$sN(2)U(i92|>MmdlawIk#Oj-&Gj`1b+*Hz3GU%q8LK$BDb2ht@i zRw~!rXNR(~>3w~g+K1V=Z9F<(lCg$cdr7-hwaZRGFqnjXmU!Tp7K5f+UcH`(>|1)9fP~rGm=wAdB@o*3&V#8gbYyeG?&wd z{oeQlSi~*N{Kkt3Y(vPUz+PvO^W(C}4`+sQp{!K?w;l2e9p9_waHCCun7h7)Rk^wO zYKqgjSx+CHA}`QtWqeQUc&VW9n)h0-o0ICyt@slOIw2>f$Z3B@S2Hduw_~xzOFQC^ zF)Cr_-ZyyBi;DC)7ao3ah(YQZeO(gL{`&0KirPGqm66^pP-kR3NmM`pDA5ENFYeY zwi$6Meo4+Q+&$LtTB06RZ--zY9>fWswO zCRtZlKqAX*?gR$j$^lx%e&4CRU%BzO#og7>fQ{?weAjE1B{h+Jp-zDC!e$Kob~sDp`NEQY42`v5=y5K>QnFxRaFM{nlXVn@;w}Q% z>7EcHh%PU%Da4n0Q(hH&e>yF+gG;-nYzKjKN|*SymoJH<$aSWRy40c{kt<(nsDl$V z1%lp^`U1yc#>qaX#p{$WDcBx7Zuh3ewP}g+I&dfIiyE-0+AaRpVg28zrB40S*!32Y z`g104=Jc_|TQZxDfc14=aDLVY=7#N~8Qp=*4tNyQkJHCgwY&$too1!f*oAQoO`A%+THVeA#^2K$AwrBxgs0G@~{f%G2z?3$^Tq zMEGduYz=58XUyu=9MO9fAQZ+y?+AFPI1^j2U%S^((NHK;P;s?9O2Z~abH18NfCM~rv)_o*QfQg17+R9eQ=O#( ziO#w+L#0_H!4$@~nL#l9$_yl@!uWK zZ9^I3?bXZszy&!Sp^~}uG7MH(wru`He_P3%Tc|9Lr2F|!E1YJ1WR_L^Rr!+JKqO_l zA68J%5RXL}<#v(A>~Q=~MgMuV+3IsQs#)uJ49{BSBLdT^G56cLsBZlwZYjfi3S2hs zlqvV$ZvQKZ0hXyNH#ZsYZGDSNmOt?EwfkJ+Uk9L&*f$$ZY9W^h&jb`nnowVLuV>~j zT>CiGRfjTlJMwA7i4di96&#~uJlnt_D-~Y(4XKcQzSyP7W3Lm+X7?^V2TA2Z&UUk! z{-Y1Y4#)};rOpb(5tjjt>f96m30@j*dBA-B@wuU{a(i z?4Gi@RBs=VwpQ3{tr&-gQsE|!Mul@S`VSYHglvSy(0FBLE5RPy(ORAa#msm0P35KN zL)`-uN8Ve3tV_&z0p*QHo@?y;%Mg`rb>@?)_1lAV$pYX+5hpkecorqBJBGu+?CpBZ60%aBM1 zZD)>`6VNt0U1uq)5H33nWoj{jKrn@g1Yl<8x|!D}$ikHjiQidVFk0I8zL@Cx={16>yT?5d<&b;mRUTxwm>Je1lM%vH}=$qK74x}HQ#=aSWj=6?Mddi;l_b6(_uXbs2JdYz?BpMJWBHx&SU z9n6jH{sj24K7s7OPk?VDSLTr0Y*~J)!hn{o13#+YcSw-eedy_T$r}hI!nrZfP9|xq zU<4e{zQl%xh;n8%NHzMKOr_lFH%sZleV^WvwNi_C!!zCS+5eq!2Jt;N)VJ+ztVJfa zsV47vNb_EI<K~_-ILT!1d zdBT3yJLwgTs}7|iqkA0EH=4;pWc0k87gh1J=JJYmzRXsYOp{kCnYWb>{ahBCKH~qf zdL+3|m;KPboVV$pbp(oTT>Boh0D*RY&i%7(QAz{$l8%W_v;@bvih~F`#cr!zh4BW6 zOH~j3_2xmpR=)_c!RqL{G<|WUszafYRb019zJHZx+Le5pp{9|Z3xX@FWKp=S6<&F6 z@jF3o|HATjk4zss`#U@`>$^BO*4JJ0mR|hJ&15>3C-TjAS~VfmT$@w9!)97DOC`8O zWm@TN$M^6Dw_6{y8d}+AU23k9tM@1? zONjVE=O6VEg)4wGh3YMhHidaE@sz&}ej=o>MW<}L2&c69)_?fo-9_{8*=)*$3bX4qa#mF1!I)uJW{n-gQ^~s< z$|tOLIj@Tz0aK$Od|$(z7##CH1I=e5Vs5Nj6U5b>NA6<&05(hz(<|edwdW^$7rh$-#4Rrpc6p1} zC5TXYZo3l!&?hrcN^~_q8rA7{ti2*YQdOEyZp2{mN~Vh$Z^v=ya_h6Sp-1TPgA{}w z--}6Ip4#x2%;K_|@?skkWJbGNwwO+#_mOuBqMJYQ|2{~QhX%OAzqz~L|21~rjlt)e z%T6C6|3BQlXIPW%wl#`?6e$5gdJ#d2H0ebG2vQUTX@YbEh!jz(NC{QxgpPES4npWn zst~D>4gr;#P=oXm2yhJWA_Y8e)UMIY!NWY>!Ky+yj0}tY4{LgyZen3B|ZDJ?cE{I}uun zYw;x<-sKtxiS0>wqNk#e^a!R>Qci@1x}rKpL)WkGeH5tP?q1;JRW*bBE4HD;B=Teg z#1JW2LMGNALRgk_2vUxzFM>U9_qj-pM2!H38>c!zK1lY+k;KEMQ1)9j+&j8($TwJk4ax$ z50k8J5Dk!m^0zzFhsER93&^BbhS_{Ya5kX6Fj{80YoEvU+E1=kjwzrb!9HkoV5&u$ z!J8zYzhy!yPW6ZQ4NbH7NaLsUSIC9>(((lFroH(p?B$T-v37j67urDS9g5I!^^E0P2-xM$l*J|&0ZhpH&XT(VQon1oSaHKnr!2V?3w})Ja z$e)nnRocI~9)DgOPuf>RS{4cl5`_PKpOo#@a}SAAvLG?si;Khzhm#J(dLROSVj6cT z=MJY>9ck=T6yLkUEjv*&A0pJ2u>!8xUfF%R?4|NyZ$tz*Ki)60{22r0G3CB_wOwns z8Z1+eo97AAIn&Y&qRN7a%6^Xy9Oe$`NA((MJg!HF5jJ!;?CY^z8(_=Uy0`b1f%lHb zqG!mq@ZD$e=BTGcms0TWQ*l+oE2~_)f+B6Gi2lspDna)SRdml|d4zIdW^ZHv2_&8K zQhwuLnZFTTm7Q__w%!?PxMQ@3N~i-O{@0iNyRb2?(Lp72AXe4==P|ezRH{Md&#_?( z$^pOL+$)=txS-Lx~Ddkq(s|rRM&g6!~bqa?0y)WZamS%}}7a(bY zbd5>mdKYBj3k*a~=+JN|iSu=5dR1<>oybn}e0*eqe6p$+lI2?d6CI%k~jNM*eC&YMfOM7+?d zykPm|n|-9rb;;0vI8%RE%OEVr?Xp=(nb!6m{PR-JjP=52vt{h57UP(OZkbnmK)dKx!kLiD_mxYr_+FT$LH+k@hK^y9kEQ+O=!Re?T5)KG;l@&-#qv!k_HKPX=EAI~FBjxc$q);}U5HY2xqDA+_jM-PO z!S{`w@#96g-q90hj6f(qVPcApFO$z%X2BH68w&bhQibjz`3UPSlaqBUKtwE~xM1G< z+onwNnkVVgvh&2*3k#xP6-7OS6jZ{1^)TprYmQq;ZXe|K zwM`&3d($}&$4llb4{}XQ=u6Z`7D-)SqE%^~+W3s?(96>xb^4o&!4!1$l1ydN^Y=1; zT_EL^e}o2TRsVwPNXM6RdGMv^#p$pqOR(>mc{*uEhC!_oj5=Vy8et_Cf^^&58Avgw z;fBWT(rMv_xs37F?h+BZ1Dvhi2ek7PL*bU5ol)BhSNp=CMBXBrVhiyf!jcRQ2erOC z8wwf-C)7RX(R3;idel+!3ZgX>o}IzV>>kwJ%a*kt9jAP?F&d>(t@fDk$$HJxa!XwN z(>znD(s%kW9nq_p#H?kM4Y!I2tL>ntO}r}0$ldA@b016(3G*KF;yOEDCFTDAeTJB! zGw}``4sUELwz%cHK?5eGv6+7U#aw=E=kC{8zY>jamWt;zEjniLtsZvTSV=zhG^?g@ z?hbSD6N>J)m!TtK-p8ai4xI4|X z=nLMKnjj;n;8AjhB9+8pCZ;@YEGj0IfdG`UrM_?ZEt!b#Mb34BtAJa*w5A zcdwdIUnO_ja%2G>Ex0FW74B|1akFWWpLB<%^iqK48?=Uq?eW;_+#b#keAI5m(Sl98 zx33ZQcT{VAa`QIN?D)7$f4DF`HkH}x zBbAUFGH%cjF3c_O%sgGz6Bf@)<8x_PY#l-^)?+0{Z?Gq>B(EvPlxzh>7hNdh(zL=_ zHF0TqsbB?9CvbO|z2U62#1MazThe)Ch((&NyR(`0#3eHpO#<<&iE55wrnf0*MOnX& zQnS+5$2l5myHFZ#l6D+ZXk!ti8E@3lktOn#A%+3SPZjLqZll(OolXy<2TLASNlwZ4 z*Y&A;@{8tqbS}$R#j{rxs?m<$tiVnFGalFn1^jQM_&<|Jhb-}r4smJ<_y2|pfwZew zGJ3ws=;%5(LW)x?_Iu4x1+AsKEp|x4*gYa$Q4(ZyXtYOK?|9ykLjmEja_C|6C2RFo z$Ca)qi!sNSP?z7`*}>>z^(*kEbnbd-tZ9z9J$LKoNp+zU9~4S;amjg~)an;z#cxr# zJPCD0UOO(6-HE2x;Sdpi;Fre9@qPDgt{TzR4)SPgiRaP1_qLCfOW!uxb_ek>D&#rF zwS0dq06{>UH15C9)49TZMRGsp;}=YM{9)(DIdRMv)wwi`8kb~i})7r zzh@Nxf=tTS2wkj82)hmb_%#m*u_LRASL{t{*v7_8L2QKH=}GiH(JKd0j_fn<&7(0m z0hi4dM{o}{L^MY(^u=0{f}H}mT2ma70_?oOT;T5YL{Ofip6DfU=f1w19CC(}1*m8g zZ5g)RnwleSw?i;Mz!+-VJ=!9#bn+}Q(xu)enaI^7&V|PPQXXw<vcY}LN}M%2vCddj8Un?mVdRbv3%1@ z;V>@GJfO~8ZiL$zF_jA{&{a_3*8!&Fg%W)In?%X~8#_k4d-La7xB&EOHU1ZFDi=r+ zPk|)y@tpW@p-B=XO?SF8vt$epaYp48;qHYh9qvAbTOmcT3wauVIiFuzv0v9b9^H!$ z$?{<_q&w^{*Hc`bj~8r*(9os%@$f08qD!sB)yqMaC)PC}Zn+53WZc{PF@0bc>h>XL`tIQ_W)#wfN|wD4rX~h<~;L6@9Dbd^uxk#BAKO^5+ArN z3lS$!DktU8g6G9jqSW~+pVYdvEs|Ly$Pyoj;906wAD@VYRfZXcxP-1>U5!3|F2Z3& zE7mx@+W3B;Vn$cGv|$jYgB!`~YBvMIo?E%DH93#=A0PQw^7!vn3AzQu4E@XDMSuL- zYB$-qbS0Kvi;Z?F)wSxM7Q(p0?-Q?;_tcwm;BxiH$&5Rd$TRWr~9nQBGe zT@a!ly}`uAKnvs`{C@D)yywQ`tS0dsig_lT^7q0Rj-p$q1+s{ALG2^Q3(omdE_O;(p2P z(Gfa8mto9IM9DEqDuZm;bTo$dv6O6NOodQOt!jn5$#ovr?K?PT6bpNCn5wyiGtVhU&6jaC{FpHw}O3oq6mcxUEc(I-ev`zYp zoAx3~(@qudk8P~3O80a7uN7Gm+OBdTtdq>^K7{cVY928n&s&H%JBqNW|80+CB3hf^3ah5rwIX#>I?}A&-W*ze z9lUK=Y?FK>03T+?` z6^qGo4DjuI&xi)-4YcD6v!A6)P9&K+8b19SOkK+q#5s1PgpX@uy(4{g%*~lt&)yVN zuvzKB^G6WX{`EUnNKi5RX|xx+dzDg83K?Pl^lOVo<%7Vx=qn5uApT zg;EpGXI6`@*7IgUDw&iC$u8wyuDY zS0fB2i<;y5Vp|3yKFD8VmCK)bKNcMf$D_;i>OL4lz5A|x9^_nq0A1vt+wTP4a*S|r zNg`{<)R!Vh4O0F_j2ZSK^SkIea=^Y9d%-YFCG>)%fanigW?ik3yH2Z;+3_V?p$I-K z(_!h`v@(?G|3+P8nm8juE@Mf)^|G0_4=GatMSb;=E*qIuxZiev9>=8Tfqv3z!siq3VIf0kD8kX&d4V%1Q4D=X9BXyc=CPg4FV+Ft)*|!>NQc{Iw``DI^ID;nf%XT?y=Q+H zK-g)HcX~KSf43UojsMv(%6nTr{Lf%Q{F|Dt(yCb98^Qh@V=s?!IUz@VUI_*iO;lo$ z?xj2G%cyW;s}Q_xl=%+RHBnT5n^I9-ahNg{Uu0e*T2%w8j!C7w<1qp4XD`+6hbbw% zDM{87M+}>z_({3tHE}!flugdpD3}ew@ObGZ`?P#!7f;o-;KM1QDz7_cU91s*&g~+uWVy9EXQnTgGd*`|QfUeE=)lSbNMZw&uyQCTS zchWk5V_^(Epe%+W?!nWy^@Py7spXCZLj-C2$J_=Y7?55HaMJPgWHzeO<>R~E6G#K?#f`UtG0sM$hhOeWJ!e1uqKXnIbh~bRF3#afZ&NtG;=H9n_D~E# z@6fy^i$(kmBzx~o=6AX&v)03j3$MeVNm5d5>~;SLEgq7@kt8WHd0re`-RJVYJ9zyP zD1Fw}Z?4O44>vx2sW>mv%azC4dfB zlR16K=uWxP1|j3Vdlq6Skig+++v$j{M-0%z4Lq zH>!v8hLS=XOR|-_2c`fY`N6MyIbE?rapT}8zWKL`@m&!;W;{b`tReN6O7WMH^{?uH zHJ+>%At2;p&8+lw@(c|vi%;pVF(F7AmN5VK%NCevU*Ka;jMDTAWak3szEK|mFa^UH5|Y08kglfk{2@1 zg<*uTG3P-_b5wAZ!sNc;*+lqC(fd`yg6${t;p89ztApZWsmDnC{oQ&VU}SS>B%^hv z_7}qVE{Tt0<}wNC4+tRX88Y6k+H{Ow{dCTUQ}W}oD%#<)XzP1n@ZHO1eY)nCB7{>l z564CBV5X2ZXo$rP9Dsy>`YkH=_nAx4eIA5mRc6#rQnTtMw6RSQV8_({jR;534`JIP36c~MRSmXvoDcishRWubLI0FoD zR`w@A^@ZuVhWBNI+j$Lp}}aDz1ef!IuGUgN;BIBgSOj!JMUHdMU^HZIM{Jq(|THEnh|Dnp-iT?UHLg6y*nXx(yE3L|nyTvv^gBT%#e%kd zgu3W0v>g;3$;Ar6XpohEe_a!lrx8FicE}}DSqrL&b^PlH{BQi$5eksJfl#sPw|@lK zXl1J35q3Xny0THWALj36(fJIZx>C@}ElcUj=Q*6LcvQobW==4AQOmzr@QiG9fbmYa zoZJ9@J5mdJaK)NJ%$Q?Z%y=sZ`+k5k4Ji7Yk8%|$LLO1Hctq1kuX4pqh}j8(-9;us zUe?^do3Uwv$(5xq`Z6!YD7)&RIzOV-!%xVAWr>W6AuyoE5vr>~2|4ayj479Q4O-Df zPwq)~J_Ar7#x!J7RbkBz&ej{DstB9QB z)+troQl9J}*RmZHXZXFpVg`oqEv`uywGjw-kMSI?32fc%aF5t7{}v(aT6z{o&bnC2 zfHzaKyDLm}&y@y2r;B(zP!(hn$dGt>C(m3~tkHzc%g!du?e+~)GIcUDw=38Cu4Q<4 z8$l<+C|S`oZnlpEG~g!D-Nyc%kDagO#GCAPzH2;A7N0J*#P{P1GNp=R(}HsZ0VdnY zS4n*JKdT+`>;R8l^Jo~S_yg7|f987ROE~Z?-r5A_AMVUzakxxpdE7O$+**R(K~F#xQ8E2J~5Z8MsfVmD`)G zr?3-FML?(}J|P?@*~6Hv_r{ZZ=UShxXVM|+Iv+K+^~SVr=VOoU?st_&R4mD=xSRG# zInKR9ES@z3_}(O|wrGo`bWs+Q3U?x{>;DzjwO;=tnbxursu2yC#fOqG z9G*;=cV;W(^TzCs)1$EiWlDi(-q?7Dd+I`mClQzK!z%V)h8sf>kIvcnp}BlYZT+G- z;v9hdGxjzIA2CP6xyaVw{5YP{SJSl@bpKni)UALLK-aVPGB{lamdx*}Kqn~D8kbH&x`NYA`$VS1uWPeZ7vc#bs-@sTmH zexs>7b|D-IRBjN71V%`bwpX`4bia2eO$1Q?qJa0Fc}isU{>561*@m`%Z1n%mq8((~ ze!msK9kJKye=LKCA+aMTS2wv3_tMD?k;BQJZiM2U(_dIb8D}7E>j|~T??%C>$EUZa z?(;hab7e1HjgsTZ)>EuDPzv0Y;FAV)K*4~@G!1oFAf%7Yk5csAx- zmG0ri%elLP9Man?A>{_C=t%B>$-_BdS)iquF3+X&&mNZ0JalNqYKyf86^dli`@!g$pfwt&^ z=HXNvB>gNohxSW$Bg**5DzrF3)oa(-4;}fobsYlkjS!x*Hd7~lE=(J;$RGa2FbPuj zYMP9G-~9Mh$(zfc9rXwGscD+^B9Z0ds3sivX+M6=8DWJRfL?~E^uuB_>ZLy%%5EZQ%+AC`TbAjZh)JP=M$elxOA^I9(%Ii&{;0a>@>E8^SIgqJz)Op|1| z-%g;rqmCIdSDZL607)O0JoCm;n6Ri{pAJm+h*YD-XcT33F3)&`Fpb2A(CS0WF9mp) z8{>@1APZsaFq(8FAZL90zt0)}4YRpBdm9!1e{ZZLKcSAC`m^4t4FuaLHdR|I{kM(c^L8p3pRGli^dI@_ZnC&+%k=ulwkh$)Q?(XIo z)l@`0mWXl_B=HfMuC`qKsPQe6HsRAVX&q6yDz2DaHC=5t<<U zwF{`Bkn09Iw&r&JVn`T)zWMS*TWJKUTF~ye80!4odToh>!CLGOedb@~k-u`rF!S%? zfPG7u<)0*ehV;LT6;DuI_Cpq@_;nfOZ?kvYH#<|McUAd&miE(8)msrVI5yRo;Es)!c0_~6z9ZZ}> zjrwJW6ocd{77_l(h>~r$SaxEvo4Qn}yVZ>Eduv~*Bd#g;ZeJo6K_GNoF(K^Ym@wm_ z1drb9&O8QwmVuE`?6|o5u-G^EckimdD9G6re7DOby0=th-uZZa{VE>6;lc!?IhiLy zZ>m@{5A)glUnWeS11vL)ra9!d&*;EtQy%qu1oJ2qm?XNAR>KG?d95tIIi0tX0F>*t z?S#A!Pn+%?LqJb;XGa0KH|B&L-NgoW4{sc7N>cR<s)W z9;=gj+opmImF%Z&rgN(rL;F=YiK#o!1TpcEAW)rZnZ`$$m;)RNPggX_$I=(EjkqS~ zH+dAa0xJ0Gz$%lVnrFD)iX;Mdb$8)-(i}0&`Fp$^SSEFT( zLVUE(pnk*r>8I~9z%0ejK{qS^Q{oI_;HtESuZ^mg?Q8Dsucs8IMZ&gba=t<@Y*^&h z2lj`0lr-V6lln6{uVG36eCEo0v-_j&YJK(kZ}2G+e}Zg$ZY^-Ma1fQ?KR5Mb&uIKU@yJax~s+TahWb?CYYFb{*@Rb91*9X;NvY!$?j+ykZ72Y1Aj;>T&xn)7IZAU!9{NRRKqqB~L%!9b{FHdZ@W{@B59fH^L-8 zhS=sc?f1i|#Fz0#Kkop}qt(AFuRP}^Y@c;FANN2C z09`PI8(5uGh-;Wa&VR{G^yI+q7g=6B^DW$rs{({nu9q26(tpWD9XxjdTE_sW);B6d z9ixWfNBeBb(RO*{?rZryq|Cm11srni-$n3XJeh(uyvcT-Fx)q;D!|b2NXtC>2$1? zjPAwe*5uSxwVG(j&`Rq|d5!&jYzZxyf~4YDuC07**~df%*2T<@YB%e@FQc|v_Pm;HhAlP|?Da2O zxTF>6Zz;Bp5rGtv6wlFP_l>^V#=p+0o75bDH>#w+S$;A)ZtyU9*8k|IN9S9{hYWQO zW7V9S`_vt>-z*~wetLc1Tx&hjc=*_^qV|iM!`o}dgHqrlY|UB~Uv}yEl<#cQ*zSO6 z>bo{c$+M$6nOklWf!NGCJ9e9{E*kQaDB$h z#%(k!s8TIAgdX*xlp)c1+-@(-ymz~3OBtr5qRWPU#BA(_dCEh=^Ev$qQWpbxyj$=v z(L1=|@*2tW_-IGs5ls&4gIkQGx6M&b^o@i@ZwwoD?~5p@r|Y7J%%v`8+;1|X^T9Sb zpqu~=+O`b;pbys3Gz6qH^ZtGm#@}ZsF%~KXvF3D0=8}Q>WIa{<~LM?Xs!URG$pcszTDbqr}rTJ6d%< z*NXi;!xN7Yp~h|e-dNd%uYabS_`LL-^HOc+JAe0^N3-s_&6vsW5_6s>B5MtN*)&v? zKSzNE4Wnu8O77K8ZE2MAPOjwa3*fz41JZnE`*Z<+942+`y-lpsjw#R#>h!)%uLs*3 zpvj7e-p+QTY!ILXVY_OI;BvkPjy!isONs(%&D1?m;?>45&_7xU=Gu%S*dVd(Zv2IS zcHZ8Ov@4NH?Hj)p$$x~JPP*w?fsSScmC-)`M}J%$D?0hTYHnYA9j{Y?eNu!qF&TZJZ_>qj{zQn#~eIJ|1l>a&;BHy&MdjF6EMJxKf6h^gEn5aTPvC=;7`rvwMq}j;X?%8~ zca4UEAmTR+*iAzgmj((@1?IRq-sO+tGU5}!m~+QYP&U3)^^G9_4mF-cId`?%88ZB+ zCR<7u+%|SyWERL&FgHy)7FSZJ$57mjBH;<7W9oc1%oHCnzY}Cg;ocWcq$}dBK5_lc z6K)xjJCL34x@i>bZ3#v_ogh|>hE;!(RhLkU=s!X3 zAl@6>pIaI4k7-M(rL*Z+>GjFKwZk4M71~_jts*YwU;vu(77dbnwv`+!e^M+@b@??l zi{X|YXOwXBs~P7#r2f{d|L%?U(_f$W!)k_fxYgUno<(H?yZssk&8R(Laaul5+@B{} zg9m(W>T}vs92i_PZa=YGSKRot*2l*IZ(#OE(}oo&?jci`EWG+|uDxXpla}++5I1*^ z$tm2dpW{l!&p2IZtz^A^s&T8`!tq6a>sB$95P?^R=;1(7Pc)OFLYD6=ZY|84-rDSu z!7HaIg~VH5N0px3*!+BPE_Y_u^{$}in_`irK;o${@8t%2A}xbCUHMa|f?0~Z5b}R` zu&Tp)4m@ArY#`%pzgq;F&qRf-Ac@)UwG)O$xB8NC+zD^>YsFccoe1bM>%xQ*qTky= zmsiJSaus^fUB#y3T7i^iZf+48)XAO#(%fF%w@yXhhmhtg0C=uHnUlFn68;U=Hl6?R zwmetBu6;@@ra3wlsOW6uF+pZ-u4f+~P@@ z^IQxorx}|n)=eT+wecPyaW-XQs87ZmyDBVoI$x{x)SZl=ezlK*+65hqCX&ts*z&;t zxaEb{Urj=#U=L+i@hi7DIiwMA@QdT&t>BPUq(E%T{=}j7#0SXdpaRvO73SD%-(T4& zR&C3M@#Nro`+k8U%`kQ!&GeV&xSFjf(YE&@P3UTAvwd_M^Y?U;6!M_`3%d}{nuh_| z(%8>>@0PN;R7cAjeu{BDmv3zoIBMetiU(6v&R1MElYJrGoZm9Zh6BDz&``mf$}|*gilg0!-L{JZjO4okh;Sg^Ge@t5sW*W1 zVN8kms)36TOkhj%tK{YopP+ZX+29xllJ`af4)^YRyqtXzLpPh&Q=|1ZLQ)k`*e*?- z#k+RUr}}vg%eq(VQs_evof7)i0!6QEO^MwZ2E)NfPMWBoR1WrgLLGKGAsTP;BFH}& z=wK4eMyVgW7QBmbZEly`UaA~r}G5_zZ`ev5~#NO02AMaFwM|A>L@}1u`19{tF z57+pBca6(2zylLbz(^kWAC2Vc?*ds&px3B`66q{ffqouK1_z@QzEboYvM zjVW{Jbio>q(URpQl45X+-T7t@)knnkrQ`j?vlv-;dg;r!#dvL#JGon7#q7(YtTR*Wu4W11i zjq42YioPW`x>?pJ>;Kl@dvmL1rNaNU4;|{WJ;>@IVa*!-7w{yehV~2O%vrabM$n*6w z_-Ij%UqUwYWO~0dj8p2_%m=jlw-Lb?wR&^rd)uq94=?q?ED(Nbvz7x!sbY z%=B5`fN|m)Y*mj-$$-aL!B+c)qJegoV|F)IfI z#$c*qtmtNniMVhTI^l`TenIN!@CPZR;#TXarO!bCd}^m8TU!D-zE)ILo`A^dUg?p% z;`$NEQg$IZFlpjmF6rw>4RRGHH@Pd?j(@-+)qK9PYSFCC*rufAJ_LTfK3br4;x+hw zfA;fMVHoEd9{#_5KWo*NXxG{^_;&vu4Ci1w`Pao*(Ms`Keyn{D+y5&5-JDKo2i zG<#uP=Tzap@RspgQyb+cOMguAzO5h0tRY0A?ex>nu^UlB=_!`a(y)hEi6I`HvmnK@ z{jvQYd!o6u|KSrK zOmeEAtCrc0pisNB-!kWSnVp66Gmv5?Lg|THRqg)h000m(#R6t8zEBKJIbjJe>ZVL3 z0B5l-c*3yhoG-^uyme>U>JUQZbV&27btT(lpuE9& zyzEm8q{XFBOTi1fpuqv})haw{aWA@NY|b?$elTuOglF+RnAkU*aQf8Lm@N0TXD=K+ z@z_iX$+1N`hB)m6TtFGKzFV#Pt9h^jU7+Q~-KlKwvzVh$M9r_CuV>2@yj~pSl&3Fe zygVN-H*3IexgDFmEC3$I^Ix1g-hPxEcPj3S|D5SLIX&9EQJz}YA_G2FMz#&6T0xCE z3#yK^ExWXY>kM1o*+(ALyc`9l&}98;-8$z5x0T%75;oE*mUyww9h$!9zvSsh>2^n0bo_jRMHjk5{?|shX7sOsT^VB)bpf^ z+_k2F<56WI>NqYwFS#C=mROR~Jrmoobz4)i7J@K7lKD~xRk6}dyQ0kQ4$?cgVrukiRdTE; zy@B<|t`xjvj`C_{Sv_A0pm;2mK2>v{_*hpw2{>$$L-5`Fd)$fO`s&{$55SbkDnAN% zAh4A$@S~NGq}jv$eFNPB)^i_&@1!n0;qB)KV#!L&yh+QyVR6^2pTtk^B_w-Wyxk6h zLC1Hq^|d9yMB6hf8W-2eh1*rvGTg0jdHBtE?j^==r0ZuRqaPSHA^$dpcwGNoYIb!= z0g4FUTJ;MsPrrbhSZ*<@5L z3F$jJI3uiy{H82U3hYwNM5^>GA3B>W9jnSwO5}0h&CeI=5(q@9dHUDbGm-MT<)P898O~Q9KJS9vjRs=8@m0aoMND}oiywNIWNT7pM`_H5}VgECfYrVDfa zD_I`EAeVxMcd$>ryxr08Ahm~XlKQ~^?UD5e{A?rJYsR6dcKb%uco!Q$gz&e2LchOQ zn_jW(g(4BI&im8vo!;KiAiOmBdlFW3>#8%pr z`B`f1l%=n<_e-THk^7AEv&p0-p`Cb{F!C;Jc$feO0R|xE$3*!HKx{4!)ZzHXJM{Sua;KzzB8HbCD$vjz`RSP@gXIjDF}o1W)!P#k>N<+FP{N`?n08gA z@C)0X-pdhF#R{02@E~&;awW`Dpc=*v?N%YV&zCm&$uGf4Xm22e2|aS*Z4t6XMAd}MVaEw%Ln46BT#m}YI9e_wUGmb>Y0|V!3!m+%54pGsWniZe(cWxOK&b{d^^|K zaGa@^%?tpz(4Hu&tn|Y=66HSBs$8m2&ntaN_Q`y12sF*- z=$Apnz&j<@jZBC>wEomB^PcNSj!2(Zq5+ZCn|A(+!gF|D{0FTp^(4mZfVSB+Sv#A} z+E9Xj^#UM!NUan>$8voV6ciNBn=&B!S%KZJw-d(`k$!q?^rOpRaUsH7?t=wdEO!jlYv@x!f?>kfOu^`8vr9%|AQ)rCLDHf>It0m2aYmLA6d34;#j zLQ?8i%bFpjBlFB~2}v1&ZPSH;Sf9pyL1}yc=WD0MDm~$G2B*(OqeHvc_s)5nDii5i=s}*wWq09^U<6NtP*%3;f z(e@${i}l66)a0S|`7iP@_w9%%sB|QWchbi;!lv%wScq3BL>sMx`Ovk%ys#JT2)2vyjmFm(li;3yU@4OhnYQoaYnppG+cq>;_ z>~hciA13##7?VvTaG|Ia0Apd5AnyJ0IYD3AfbL;qG0SkDj`e@8u=lc%sD<;EQzx{!8YVm`qa#WQ{FvdCt>nOi~JlEK`S4X zE8G^ukUVm9fnP}UwGk!plihcV6n25W1$0W&A8d4%BCx^b14o4a^VBDmmsN3c6S zK@i^Zpn&K+@3>&&s9i}AddZ2|i;6cr{L6(dl@QfW85WPz zm6Y9%p#tbHj?A7Zn*p-;{>7_Y(hSWar8T1(`? z6M8yWQ=)rzalF#LdtX}W;APyTrF~X^A&$LaF8kd72%IbnoKDsselGr}%q_4l?BqP}eB<&V>7kp0DnZxLdS)^|a?FS}P-8-BQHDf++DAUfFm46R6A7Gx3f z9ygpi{wPxP;|DFAibF@@XgzpzTW{K6ln)j@B0iGN+R zFCECCvrpadPo8B9yl7tIb-)-sF3Gh062S>lIM&R zj#pd?{H3hgIEQVII?j%I$PF)C_Rj)E?X3Kfb;-AZl~(z6r>O47o0YjeCwgX>?u_3a zQt+bixkuCdm#9Y%u&J_vFvRg$?8(J#R3 z!9Bq1W3Ot&4JOA9q#A2WJPl(@uvCEF@UU6>$14x)kxfW!%l4noJhC8tv2v|scF56q zid+7M1~!N2U?#f(^STAnS6tASS;^p-BxY4pVnihQh@-?u9L{ZgKcb$<+Z?uA}`NVx^`QA#(R)zY!=Hh)GOh@7?IfW#px%W9Pc_?xKi2|aHa57(It@Po? zC?R^QNxYq6L9l^f1%5Aw)%ofIt=jMAz=}w8)R2;H562N}vSJ^ri4NFPSurhGJrV%9 zA3HUIgj&ZF9Jc%q`!;~N6G|1u?aq`7p;YNYr9gv(DA9DNYsaNzUNRj|*_F&aL8kC& zm+$3nH6V8ujc;K1F9*5mGLDSu1#_Ukb3b!YPP`&+zh?ara2uh?45rT)gg8$|wbpbQ ztxShVIxn@OM1^Vj%9JPg5|db0xz#CQq93!fHv2ihJGZJyl9Zd#x)Syy8=`%?ljwhw zmGpq~%e(vkl<~MGyn70rD^lSB>Bb@_OPpgHFDc6y8|J>0H~JS(Lxp8`KBVqRR~Cx4-{9+j_p(Y-3Jm%pkp;cauu!45rZHc0$gV*m5#A zwXUX!uk7#$V$lu=_{yAINJ*IyY}OiB7mQ%AkZtFDbt3O=>9d1ao49DT9P%4^%cy;N zw#eAz8hO;UH*^Oc>F!OW^r@$P2PqRAf-xwS*W865ygn? zleGlzbQP%steBI0b-=}gyu}=#oBC*D=nJPgv(mlfy|2LBoW%oF(h;4b1JbF$JPLPs z_kX@4Qc7_f%8@(WjW!2daCGj6%R8x@3av!ZqnFL5Px+`rMMF7Tc5nB*Ya>uc$9bze zU&r?%_?0j{Fs6PY&;m$61iGDWL#n$;)8f31(HHUnT-XKF%w5jW+EuAt3&!p0tr~tltJ~>!-F3HpR8i@rQ=&(mD7*q z3IwM@+ZP2y56vGvfjMjWdWYj9%kb4xY^>hjW;1@fRyQ^ID(@lF9-F#0)gpnKtJMr= z0Je(cVmt1IDtA^bn`)ovq|BU$!SAXGt9S<14(VY1RLsn!>i)^}x)a)Sr0J}P+DAVO zkPq7>Wq#V``Dt^fZ+)!6;rB$te{Jg>Pzj6u`!cd=3}AFKrCPX00hnSVNIWz6(w)A^ zjRG^0;~8QN!i%!gVRajHtc~37K)Odg_b=-z9%!=@age$JtZ12zK=EX|YyUf)ZbhCb zAL~WS%M;vJR(o12>d!9oRB10i1r-QGwyI7mlIg@tb@ln3LPRqzi!>eUt|3w_`*;&~ ziBpW0c*39n9ou;BoG352)DuMdD~G|#FG8VpxwLFFQFqFZ!%F&{!p`y1n&=dDl2+DE zej+sES(fLd(C77j!LhL+1!Ik&dRMzW{Bw^ z72kC9;p+O=n2UhXNGI4D(e-JrT!(dlOANvCKLpJKqkbPM%3ZdY?f?b{ohNYkE(&ye z7M}`9teBI@(E|M^U}by!%*+>lc~pB5qJufH8k`oM6H@vyubumseQ@c1htu|BfbKJ4 zt<6y-asAZ-54BDvfS6Vze^&LdB`>G@{%X;6KiSgJ-D;iNUYYijYq-$Uz`To!=hn!V z_$FI+HBYP%xOz7aS8{w^PUbdVPEzQqG8cVRg(>W6`naREBOe;PDq}Jh!<=^J(Y~HP z2q>>+k`c}b?hlZ$BN_lkzcauKmU`rjT!Aq^8{=h%diMilPqFeF#p31K8Q(ipY-aEC zWsh6eFy4JV_ae1+@qr-b7(=fAVrN!gp!rf^6-i3H_irK@nqazT>R^4Jp-$|dPtjW9 zYhEkyiX2XH{l9yP7Ko2qy)aAH^-AdeD0_j>h+9hd=Nis_JcZO5z zL;ZMEMa&Q?Bt}C{`N-oZybkZo*bN}B$O$4#MVML{F2mn`QQc)+jYNd&C=xOR{6y=+ z=Ylb!f#GH8l`FhY1+CO47@h0YtTuIZAOZnvx_&ILuH!3JK@Py{V&n5sot^Lkd~aoZ zG)u3Njd^Qcke#)LvvtJG7|8}aQ5yA16}1oV3>o{W`fC6m3jNeRJCj^7yL|kR|5yI7 zOheGyigW+NpM0tPHOdE+L+dkdCKpS~J|!)uJ5}_Hj^IZxyz$j|wH->7qY@IMbjs$Q zU$OH^3mS2bPShPW*wH|*f)tacy?c_4O%-2@?|mA4xy1NgW+^Qe_WyA9o>5ILTHC0K z3W^j(L25v@A_CHzl%S}nG!>Oj5Co(~dI=;V0xEseyMlt!LhlfeUPF-%K}tez2_c09 zzNPOy=Xk%f-*LydKY=kA$y&2MbI#{cvmgL8d^|?aOg+DlLuKz!=X)gQC#vN;Op0o| zKTxWBRu+=KW%}b?`Q)ij{FY`&Yl~YWWm+coUoPNFJZW?L&uNeLJB0zr5WL_~ykihG zVXG_FZk^)SUSeg?1{#KRl=&)eZUdrY{pb!ply`u$`&Ry??X(`ONp1;jX%6*mhL*bS@szS@qhJTBF2uPlH4`N_v&OzjjaLP{_K7$hyGX9Th_OD zJ5?LsYK>^jwhLP|hDqAqiY*Pa$!s9$^oZ9v6)jA@EdN=y(Ec*LV(i{8AnF;I(#o-t zf-*~_lu9pF`glYOfDQPUsuv>fyZ0;Gae@MV46fuOQQs802<~XQ=D``)-Q~d*;C_AYG}vU2QSSj*|F8o$&7d9TGx-^mJFn}~Z;dUP=%75adLAz404 z)IGmoy5_r-9t%OLK3i&yJsI~&jOMWOt{{D%*h5%a*fCB#q;!99@z*g>80(=PdbO~u z0J9EfxkV`4-3%C)Ob||KrS0u(R9UamQepcWRl_T}hmDUA=K*2iR2oGE@<4reprykx ztP}_>i~_|F6aV)qv93yIad@9JBX0yVDR4yI zODc2>qrQ*d9+K?B0Of{^yx% zXg2vKE3z6_)DAl1v?-c#Hozq#xGlj#W^qJSLE_RIV1k$}o^+?Isr2Ct`}&bLCp3H8 zm|qus9RUbx4z_E3$MLrWIp6A8T1RD6le-Lw%ec4K){0voCDF1SY?rRzc=6%Be*-&! zdF8RL2M0L}+}I8dEjG}>Y5;^hs`FRdb(+x=XMtOZ`PG3sOe*+HclH| z5;g^0fl!xh%`M+0Jw|^2hu71Wf-}5K`ao7#lb2^q-23DW5?}ItyHeW>3fPVDv7HC! zKaE$;tuH9YUyBESz4pQK<(aue5-+s&%i!IMDS)=C?b?rq%IwD7T-%J;Zpm-I;OS>i z!FN|ls6%s?&X37fKRp?S0g;Oy7ET*J8pxMC=W}J*+_*O;%!B$25gTMDhe6B+?4)%G zeSrmM%0v>T?3eh59&1a)A4=t2b>L39b>KQ?&f#zVY+s(#*o|nrkf5uwUTvN6H>~OP z@aMd6xvBN?Vb=I;cCim8xEb$RT|~nia%ICClb>y3gS{|RIpv&agKiH!ifk{FrUdDr zFC|F#sd!JjYFQA@>=ZK8Hv8s?X@L@2T|dztWauJ` z>^~ndaqt4zfR0nEX$BeqJUd+6h>M>iY!Z#HNbo@_O)LXXtogH2eln19Q8PZ;*8}!9 zD)oSo%QGCsZ3aNXBfRfBv^VXM7Z9n=yg=U;Hg1>Ud-nh@7}c+gOIm0*HMQ=(4`lw< zxzHR6E)$%%hadPvDG`YnQ8rM`xF2QSyq5(w|68}GJ$gs}CZG5EpnX5N^s>jm&Ctb? zPoSkVcdvu+s_7rl&Z0PZo0b|;&jeDIXw(|;$|M$~HP#j%cWWK_78ratB#2GDT)cu6 zY0KaE^~SG_DUXzbbGeJ_k6{n=V1ToM5AIOp6QXe3ukc;=0H4vYeuA0!#$g!4i@(1@ z{XZiz$J5UB*=|KqE&Xv44wlCLC3lA4`&*C+k)N$eb1k#oTl|uZ-CpX;2H4x^|7%h=qoDY~A`sLbTZgO%8KY*Bx?8vFld}!ckA;wbJRa$xH z<4a3l*-L~7=e@CsFuU0dW~90QL23Y{m%uI7PaJ47Rp1QS?Z3~EI~gE2NkVX*@9pAB!3$r%C+GXjC)RTo zv~?y1Gikh7PA)SFe8+cfqdSd!MBIzqX;G3ShH|jFUgvgO-(^B9!|JOAwu3c;Hop_v z$rgpYiyh-L_XSqXuYyWymi3m)EN1(VyccFVH}06%{(!!Dxn>62s(_g|?X(d@L1Si~ zo>}+9SGbBbEF%nCZKWdh!!4`x^v2<~{V>6R?;^9XZ;L!g zZG6D^w@zRE#H)8*0MmygPC+#e~l;4BXSU|YQ{UCAaOyj<9USnG2TJL9W8?AAdN6P&z%ZX znuiDbv$PJ;4)s!t>uwHx*6seO2j^#{#=YvC#YDdQb9C5dEeX8q4Crc0{IPZg?Fp=H zrr6_>ZA^kBkU`sq0;{;r{#0m9gOy$TKLPDct-mM?IT)~&U4T|_WwWbW#P=2Px~)do zLBR(eO8tr=N?9EoX#InO|TRZ0H&W1PbSYbGI;TMRuGu#Fg?>!}IX zu+A-y+uogdMK52mO|=;u)>n%!s-;RMZ@ps?2jF+OUj=Gs0j$UAnO5?<+|6r@A;SDiHad3!-;E74uOtX&VM@)B36(+RvDH94zhg z$AFd2(;ro&dN_DP&Ipvdi$A|@1{u8`+~D^vLU*I1j4LXwW^Y42_x)SC&pj65?GFt4 z!^{e+LY7Zxma;-Y=OKQ;@#u#~lcHedimtFDR6q<^rEDqp2fZ;mBYa2mgP3oG2j9hG!7;&CLufA`k>ek^iif%qA1OYl z>_+;RKFxSsr{vxg)b*$Q z!gllI5~r14KCC?q3>7;_b19><%6BvjHoZCwAT0@le0!NAE;trZ%E~wW)}E$_E#Erq z03Vv~D)?IK{qLUl~DsIUr?HN=n&wfmq@N@c4&;4&*! z$a~h@xAiPqaAV3lsGN3MY!Q6Re!F@(it9@Z6lE%M*E$x58I$R6X5=^ewm)j>az9Fy zdizemVoWGDo9FC+vN{gTmz`3LHW9F-ew z8saP=G9^WUsaMmtr()X;O|ol&FOzkb`2!#~a~Lr-@9=R_bU~H|n9z-g2z0t{K_}$! z8KK6*&cSjbpvoLCIxC)u?q@t)+dLV4QDl<56flfd#3(U$Ynh!gR1ey?3V?)w<|(jd z!IS%b>&el@io-U?m#RRjO*(VhU&Mpwfi|zvJG4a`&@^(vM8k5F=Leqx7ZrnqFC(xm z?!R473tKv3KZZ_n9$UVhp3?|Qlll2sjv!vPD0vFxHGZ}xq<^R>3&9|s2TW|aqwm7y ze&d1mQyjr?Cx9wVvs5!AQdu?I@5FiG+ZVtu z{>{-4C6VaI_kt_yfu0AuS2R9MtEm3NH~496OXXkdLVm2<9gTmzAZG!*C!+Tv8}Lt~ zK1Au3-Dh^Lv=(NSGk*B-I_IS(rR29KvV!bx=hnFVB_DsJRZbDQJ)d;t`g)wm_&U<_ ztLxjOXuNDziy`Gr;i5><>(&JAzhDlD>G(CAv|Gvb7}`5+RgYRqYhYY#jl&hMWRS}9 z4o#VomJneczh6|vD%4ZrC6O<6RuX)0^95yn3F_}ywwM<@q9hlV<-VGZ6}cAt479Hl zP;*n<59YG?vAp0|s>w(lOuHMZtFW0mY69Dse%LH3%w<9DxWRIv1B5Vh5tvH^Oeg6$5T)L;5(oN%M`Z&vKD zHHQY;gBeJ4ok#NZ$W7I2iq-6ESxswwRd<_mC=1-ND5dS!;S2~`hBxNKuXQqXExscs z)g^A!#BoD>o@-nCI^F%7OB}R6ppdn6OC6STXP(RPT}}#uwNZr-plBxfYqXQRw5(1) zn{lEo>Rf7R*Vo!Wo1efY!cqpJWV?XUCeZuxe2|aMz=U180j28d0?>|MjXXImqOW|3 z*vi3pFJ9q*5@Mh3Io9W%VyN^@>o7s=Zu7vyFW_w+nAu}{=s!-fHB5gIhF9J$3$O-G zLjrc3*Oq1blGAmQPHv$?Dth$25Ik@MEQ6B?;7?Va3vqEb#K2z%MuG zkF~E0h@d5tInAARehBX+3+`RGj@ljC%}lf?N~+2U!~%LWJ+QObQxD^tSfuRIiTgAg z>plBOwN1r|tTI5t7-&QpqMzOdhTY{OYlAG}7m`%u3}DBYXUCUJSDX$2GNCpLbr_*` zJZkCug}s#(P0OMNf9kQ~4z?Qqix;$Dd*n-YhZbI~kDy1q9&&5B#%KBuS%;zU99%Ff z*=t~7b#j()=K8iBfHwiOb_%T$`;u{zt;z!K_b31f@L;wu_%9U)`wOD3j&ImCnXj<6 zX@;8|are3BM~ll0z9-4+F`l597|c+9ylWpiD?%Yfb5qFG9S+f9H7x$k780R3k1h9(Da+MMU#uk81Q zPMVeU4C^DDZ3$g6csAgAgE`8Vf)A4%x(qDOU;?0VW;bX?3O7_~1DeFqmQxqfq-4;l zghiYHw`C8=H4B?)dSj!U(C&6gmytQX8@g8%djY+kg~Ba6EQMxu`I0 zIWv8PFyn14>^0NBAk%4|%W_ZEFQh#xmND(wBmRINXTNEoGkoF1r-om+Abwx6#FD3t z+&m8bGh-A-o4zYv){8uu3J};-dZ1}H9Fb>pYn=Y})mv2Fst1pj-7Gd#&2ZT9QN7?X z9HID3k1`J5cOH5mRv3rbbYMTiHLnv0niGJP7hP>d@yn;0N|~pVtho z0J~koHB076Pl2M#lMv(2O`5hID{uW<+jh`e6|;n!df*9IH093WxHUW-{tkLxLrcK* zWr~`>8gL0j{EpxOpT-XW6CPaTn1V-uDtE)s)>gVPuOBvd~V4*8**CKd+H?vr4 z*DJf#F?Tpk1c{r&B8dtf%RMXU-m^Wl&rX)2plpwxKE{s8P-nouK9JUy2VdP1>DfviXM~SFYd3ZR~G*oChrbML0DJ9$G$@%Rrlf@6? zcIHRSY)lo3<7yC;9!TrKy}pc_lw}boli{YvjUY2%6y(Wc-A&u>ktlmInZL)JGfkoMSuIb8oa?9k^07c{HV7_(2kyCYZ^H#!^xB)Jy5CVtK zdN1_MDAib!V)#`vwcD#6=7`;FrnBoe?BO)@%;p_iQfR&H4g^D2KxilkP>Rm&W)WvF z6}QQkP?X%?KtYV{zr=T6CNlD=ADwckGapmk9zpt7ka##Y5{VO6UdL<(*#;8&RiJ@6 zZwyg>nWZhPDjv@T^3ML`)<-<>?b3!!PGmqJn!6Q97BiC45KpAi{B~(Ok7HcjY9H?w zU%l_bBS+*@R<-pF^Wj#=0X`9rA;B$U=JwJnynShrDqafAj+S$Kk!FNR)blKxZ~2ohr9QT3*vju_jY9Y@kt#U! zmK#pnjlT{0zE=}j8HjyNCl${8obrx=@3w#v4A$0St3NICZzbZ?>WP(Jy(7KR=u>_at}9 zk6jHtTtbAC-3Kt&+~=SO(@1q3xp_JHjRrf@v73TS#{&h~kN@ijo$JT!EtAdsMP&SV zjINyP>x!ya>5W(e8PZ{i?^2o6_XeZlc@N8%!S@zDL0f)fDzK_!h!tkYRn)=89b(k- zv#TI-(|(=l>FN_?wJl$04WU2Kv~J%UpX50vii8vMRd$F(4f0!L`i~vpIVQgwNBU5- z*TkkWhp{#5L)twtnN*z)HWJnu3Uk~VnaL!RJ77qC?#U+nFecwOBy3R9)SH^!qY|l< z#eq&%tMlKr&b(+PvnbVwk#n89LnLjmZ0x@6NMwKzQuF}9O~b?}1np3M@{>5q06saK zfjNZ+A0{?{nbIDxh)4Ofy3H&(kN64G7^Ytzbm=GL7iFlOf7d+1nc61~RbBpAwH<#A z*ut~igK9e5nL`%_(>I+u!DaTak%mR?)$NTQ(n7|F{by+z69(7cPCVat?pecZhv=zH zlg^xH-p^1r>r}9)TY68%$e5|yw8$5KOnrx5Na8W##Lp%XZ7pa_XE04NW(;Avm(I6M zs4~o}f-;h2{rcA`g0(_ehtTpj-pTr>SPJ@w2lIgzibIU5D8!Yjk)qisGC=c`3AIi} zrFiatv^xqTG|B$uzwd6PodyTLu+lVhY(|*<;FKHHIL`?17>QRxB3W8bPV0z#g-k|p z1U2J|9eGuHo5T48lZ2eQL^Zl2bXDh5W~tH7gZOSmo^ik~N3`kqujM_<$K+xI6x!M2$T+_2c^;!A;(*@Kfe9&ZX+o*TMh+x93V__-@1c0@al+ z{Y?+RabXyGzl`$*zorK@#R~4?A1cSK!b-b8Zg9`;TPPC>KoZ^c!xd=Ha$p5a?6n?v zqugze4KXC|MIT-}_?AfeDwHi?y9Em@LURu_;nyJayaR@VR*U)sdtRePNo5^HMN0_k2ocPDPW}L#-8%;N^z7OvZ!e`mbY`SeAH= z{~-_@58M%qKAEudvC1c4xT(}k({;o4`SW94zZmKL{gi?pwFv;RE&`V64epZPirQv6}IPk z(Xv>rJ9uFY9R;QszfRM-*;IV(MGN=c23hz3))}RG2+A01cmS)xCYb)r`H@6;$ngQ=pFc1bnTO>~RGm4T0fi{=aqlBF; zxzTnu)oOPXN`6Iz?~Y=&W6dn$X-HYY{^b?PZGU*pO>RFJ%Ybm`qVe#@H=xQ^>Fcs? zPG1xRR9{?QPe2A;?7lXI)r!pUiB@$4jT%m5 zNO+CDF#}m?{20kZ8H8iQQZI%f)m@S*?o{%+fL{AGpF6*ijD4@=bc9{pW;6mNW9vUC zojS&7d`E2veZvG69?cQTUF>^-UG_6;#yETB=wdkk3ployYOe|3TW(Qcl$9Z@_0&7? zrvm4-*10AO+B1f}du16GvrsDEIDP>{|4Jk0g15-u_P#iKke9X8E1UK*4t8@Ba={DS z>Hd!t|K`JwBE;6zanj6WaGYg-nsS4o{PQ}xmuccm#{Tr?Q~CPFmyZB~Z`B41BzZ^; zGMX&8diVUoPJb7jbsS}See-03)(_Bmu+gl34_kQlmjgpZiC$DdQMR+C@BY;6B#dq8 z5#&amncE5$`%QnMW}SjhloX%2goRM&xq7}Nn%5WsOe6csA0K_-HT?)40WpE8>|<`% z-LfB@{4**4D??vA0VHSc6GqzOaxCK7Vu~H{QOT}<-EAYyu_yD8 zw&YjWMkiLr%h!$pESYUMVpIqDDe3jhd&s^Cg(BI%G3q}@(t?6_HyWmleV%q9SNo1n z3%o)!T(-vbCY!f3Tc1QI`VsmR6G@Fr0S#9Y@9?)rDk=~wLf7TI&0Hzi;4%KbLQl8X zl`X7MlBOeLHH5lnE@SEk)eGIqO9$u1A}7a2U-k5sR7@;og-Jeb=%jq&G1`L>3oszk zPRifNAH4mCJ`0&4t^VI5RpJ;}Rg3f4+D3>`LvT~*gT}cK-r68?VdZ4$`fh7FjC@$s z*mzKuN%xm*e*#or3Ljh84x>uS(+D`rnW^v7Jd3~GFp?>7p9MCtPQbM z>QA^{{nHSOt~&VQRD9rvHAeg0^>t{t|1$W(v#;p&1N>l=zsJ4Uo_nv>F7t-?&M|vQ z9&XdPJa`UZC>p-!v|^NdNLBxhsOvz{64|iDcrkz!XVA?(3ohfb|K;eut1X~|hgFEx z>@M6o=-?|HI{(h?HRo?{zaw89PJ)Fghoo|iHoe*K0sr>)Ex_9Y?kKo20Vd<_@Px-m zX`!m-J6pXjk6f@uw{qgr>+%Y%kc{M(J05Ao{XU3V?I=PCquMTo@Z4Z(pdW3uF4X6Z zqTxnPg6Z$Ue2}&ofv*jy!|$r?Lg3G76KW}_k+lw;1L@fJ9ef{MKVN(|&s}g-@72V; zy5?t+TP?OB;Ufy(f|}^|o#DHg*H-bTZ@2{v+$;(hXn(%PAgnMxpz$V3hQQ0qPx(=` zQ*o2cJ}7OxcQH6A-d;ariV+4#&XV+O%fMkFC5MZkUsJ7lS6+z z4{azcsJX(m8HW$rLZJK)8SNyGOqs5M&w*seclubahFtBHAG4F|%6&AP2ki#ZR-h6xUoH04rd&eiB_IX9YSSdWiSr~R#cy$B)t zYB%e_RsHcD*9Ovi!1Xy#?>}_5EV;S#Y1_nivKxzmZEozjKMF7(M8nk&8JB~d@t-o* zzSBk_&>6qBRg`C+{Ki^@iT@72ZeMP?mtW0ZEib1wgZ92dZj3)`&KRN`wXqTE@hW_A zZ}?OR_iHSb+>*Wg&A>tr*W@^O;<`wVxA~8uJ#1@tSP+fLKYE(LTH8=oEDqO z!n8UzlS!KPTRxy|KCBbr3rSYnPoZ{1XI=v`97JDbgvV(7BC>h%@HGR#VNU#tQ4h6# zX+gn1(u$vOFXGLz-~j?4eqAIl5c{j^vN!~aTo-emV|UAbqFmKsU^)sSHxyiEZEtDp z8E=p6V?@=A%2?mCJlH3w9y#HdqD6(?<38o0d5o=W9ELRXK*ZI51%JGJh(6fL#kR)k z37mTlxXr!M#>RK@6|eMm4@ih;?A!WVUzwkv!k1uS(txv!G|6Dl3-nDO?(q*ie8UgWK<@1qw}+JZIVHWfGqWlQ zSxhDlvhw=(p(pEmJNqCLky;^9CA-c2fk+Cr3tAYz(U#?X)O43D8^+-FVln7v-`H;s zNT+^1sZq)EG8Uy$A1-ACEA!5VHubhofBsm^dDTBonBfT%L{aiJPBCLAeaCU_u?rvF zm=^?EWS4O}%OG%TDfIK~>_w_I5()b&ky6poPu`}MAqXvXiA8~5fU0@=NqyjnBLb{) zf8Px?&ma^Ol^j=Hwmh&5xZJB2#B#xE7kNkx*+jA!A+{Mn*A!>*c29*<43UPO0)ER) zL@ye(&j;*N11d_%QV1afRYn#A%@Wp!O%A;N)3haM^2!*tqkUoj3Tr zkm)%V@vQ@-aQ8K0s9kOGEU7UNkyJs9NCb7c^IvD&>hkY4$ejFE)ZnGpkcA)SN44Q( z*Vv9im&x$@kTBV_by0n|l`loiV({CQwF|dsuSk-IR7vwlz(;G)8S%mkCZ8BMNOhc6*a5O#PFmH!h6$;02nook3b@_r!l1c-cScip>88< zH1cEIlIngkb}NIPwi&-(nAS1}{xPNVvcG2AJFz@Z07ZOe>VedT*rj{=l|sf$#T$3O zNRmcdq}fKA-k{yRr@qkWgbc;cG0STr=}MmiPtK42 zOs>2bdg5gH9O~}{F-9cT0s}q|#ldQbt%bh(%H_m34t-VoKr}D)%csEL8v?5n`cD{` zYn~awDW!(a(>~LzDsjNYEz6^*m!0#(o!lQGG5N^k;aKo_*@TmvR&G6=JtTkoiBA}> zQtE0x+kH(51i6I;3nNCUbM4J8XwE78_45`d+P~eP$K7k9qM&ZGR+DF{`is4bGpBML z=ISs;&L*i1P*1bluRsRqk|q^WopAl>rWgZg(VM$?wP8OlPlhlBYdR+D@D(A8#|V(d zSaSW$BEFdX4t?777DrUmPZseqhZl`215MwP>sUnNH;7jUn5+=5?RGjR%MjQ!bdRj& z-W7IU(Bc_1dC)Oi|AMEC-?2&Bcub2?{Dy-p;1+|Lqrvrkp^**AncUwo0>K9d%)7W@ zJ;Z{rBB1!#0*~e}o$Zc{V3M-9bh7Q#n>*Z7-z(0Z-#w$yQVj5m!ewm57@2O$DL5Xr z3K=*%>(?7KUA;^(L>|O%c&tvPgj)-EtiBJb?+awDQHbAgGU8>XKk$tHCFiIBL&wYP}F{n=r9G~i$Z6aK9#vyT#xhYzvs3Br+Nq>ws2 zY>Yk}Xwy?zx!Ou+;RYRSGu9%03=;PTqu^Py6{cIXR{lEBsu3;cK>mA4M}|}Z|5lm* zP;BP(N+Z1x8vhHB;@=WWBr~otI@s+%TUtZ1yW=NpM5@4 ze`wxV6nwdbxzQyrLY@z$#A8%#MKCA2HwkQh%M9{vB+Ou;L=cu)7_+lqo(pa;$n@T< z!G1;}+@I&fF#c2Vn)L9OQ+|4-=THnd<+b`S39@af%VP+)T=v29=@jCXx7h9tBFWW# zsZW9n;zs%Ekn{|y4WIU!@squL+X!NKRL>~@ctcroO>;?PhqNDK}|=j zYR1d7rdD7Yo$l=(YcscAIprk3?LeZgx~KRR@ijX>;}mdi3}K*mY;9L>-aYGLS+o6} zxkp!#?T|5gP1SGXyJL_~8;7DB1O^~8dHHr|wmqqZCYODxZ>mLV7+GRTS8?Xa_1fim zj96ZRm(pFrx`=}$ZE&8t1|693!6%|g*RtK1mv2oF`V7sN9Nem?z+ zgA2$sV}OsgpyYXz=A_>y5`2RotBEtz)1wIz%AWN*!8eRJ82M}ZqQV;)@Y#-CeuWJD zGE}~I+GpI8A(?rrBSGoXz0UCU_S?skm`artq0bAab;aDv7PUeI*iR&LE4POC7l{xq zY9cg(z58_dny-Y&;LmEoHuAobp6@hE4gb6aw@OAu1fq5Z4R|ZtJrVTAf^SDl;Z8!% z$z5p1aK3^XnCsnu;DJ-V!4L0Paes+XxKWO@tpPS`x)F$Z#44LQ0m4tL-l&rJ1@?v> zGgKDvU9{u+z=X`6d!YS})OJAQT8igVJ@mCnH22U96TOLInO6&$YI_2B5*Dc(C!fi$-SKv_kS}Ynd_f@X~@Sl4>Fei5#hV>=w z(&-D8an%b4w1B0$XR z^`vqM3U`y<7QXTV?(~=E_}N7NRbqt&tuKl-!&upU3h^kBKZYUQtKCjl-Il-$;dZ3_ zPYlg^skp%)m;)T{9?@XmZ$4}3bR1yibrzdC+M}blp47Ksk_kT?W#o0Vv0audmJ8RA z*j^34!%E1O6@ibFUnc>SyxcM-cax z^)l}Zmkl4ncL)%pkI6xf_aNk`!|^W-08-Vx1SSPj6VFwNKWt})6vG-aUM1>b8WZeX zfmy8YX5(5BJ|5}sIgT0{-x}HXFnsX%>eyHKJPKUXWO3=gA$hPz>J&n@nbaFL- z&im+aNdJM_8(awV%tadX-&lwXSUphsyf}Hef7JQ$TzI;yZ~wYb|D3UGIiX%4okeuq zv84b2boQoErRn^#cKg&R@^iSAM*H(CAr0qO&8*P_AlN;S?TbY@ul#Jd$F8X5Qf;Xk zY`3%PDAGK2SQV!g8iiLtT!@Nbl6mxEhx6{IXiHJMh@un5{JOkv@_Lmfg+q_qTFM^= zK4*KV@F9s!#b>PdN3V{KEy%|E%^ znK})wGy@lY`)0{%OH`K`g#;Sa5OuACA7Lf3by!NWkd)L1PC@l(eSQnQzd+*I&!&e{ zsD_%ccfUtP&%e%r8}?q8j*OamYxB5W&&zB97zgSf62e(VB~?by%_n7TJ*fLzD3t%6 zY^M3gi7un$pnseoo%}UHI`c$C#1@z!F^=74Iri_;yy?kp%k7g1h9eE;hKxY8s?-x^ zvfE<^6uBJ%ec&Rosj|6x3z(G;9NAF0EBy)sRSj5&zA9`8V3-sZ$q6{uzbISTCyrA% z=@(MD9$LK|Q)U|yH*yz~lNBW~rdqjtJzNY?ZB<0+ z$hOiDyL)nwYUz!EuKm~B&Nppi&j1Y4G{NLCZeFJ;`8{?@M80`0FuX{6Gl_d0+8I@N z!%64`?soetNc=`8j?bF7Z^GfX>g2v`O)ppxpHKoeD}rs3Chvv4o zuB&WTTbp8zsVa`7!TOc4ydntL(V6eZH{8vWbj3tED%^2#P@wb&W0b0%vdoS7Ev8_b z7)V?4h%=8Am|fy2{Z%M~eq|CZ!^2gfc)Rf@1KT*#sdmD)fz~2jKfgRRd*DUEQcwbV z{Qo>BxUUId60s*Y-ZJU|+-aIAumAnJHQ19%nRXj)E$`Jn>fcrlC8f>685YwT%J$AQ zRt&PGAK&6JWqn~UF8$0##ld;tX6MhoPi&6+$ID&s_Dh|tFPU56ma=fnMcr^8%>0O= zpBdK}zu3ZEQ!j0#jZ;kUOwp`+)OV6Q-XK#t_|fq;leFL}$8|xCm%a{7tV0%3F*UBX z3#Zv-{5#Q7*S*?D7*$^2LaPk!^UE;t+O#h*yfjzce}}ZM*AY?aCz=MO%Ht+`TfpADAUX0P@sgX-`p|N7d)@55De839|iU zIAtRkC8ova>f;UTe8nP89(Yl*lp(p{a?GCB{s6EeP78Yi5t(TQwfEKWPCc`y4V;+M z>Qk2N^uRGj=0y>Mr&4r!7E! zhv+#iUk&ZYtT4Y;-LmVeQE}{_7lJXy`PxQhe3C4?K~~^YU80`>)PRK6yQZjuLB%Dr zH$kJz#+t1*eNv&GhL5-a1@^z3-WRHs)ePM1T@(cQ`HU^|S+!5{jaT@aN2ZIrt)Im~ ze=~$H)QTSY_$WwzF=v8F)sG31s{UinI?byctqOQ@MeQgiG)&0v=H)21GccD2H$xk4 ztlipDe-~)sFjknRL_MpeLinw|Uid^S6_iX#Zs-b*U5oIZX;3Kc1#r8zOFu%JCW-P& zrcNcs#)S&X1LCf9qGEWpu_d9;Hlah+cBbekuGVij4EbqAyQCBAkY8j1T(a~8+9uX* zUQ0Cd^R1zKxvZMpux~%P79XLA`C3qq?+zXVnHxjU9s~)5#f>oj0*4-||Cs5P^o)D$ zhGbQ|_Nt@bX3Y}jOA$`j&c)2Ocl=`=_wpWqTql~tZ2~q1)3wdhMX&x*9{kH21=N|w zo7@HtX*v6CQ$xk>aC>r>BHQ%&{ZX`eqg-71lOMpi@5{XO=XZa+J#mg-Q=(F+#9qpc zmzMVEIf{tl?G`Omn!Mf4c#~!UP-F^?E-TG0$#Z289PN7ZR^6l=p-wiF=cY1_dn`f; zB?{_(3l#_L3JG5QbUN2M$aMc=5}*|i&UCb+7qplRrb3J_L}W0@oqf@A@~4zhPaQYD zGdtAVdRdUjkWXnkm62+r`msrTxLlzVRxI{t9dW0KC4uOj)1M-|rUIfCaQ&%<6J4KxnoqgIu1_l!V*777Q5c>);%HSbu-Clj78R+a5P(vk|x2!aMS{Cly zbwYZ9F*(x+rx`j;j^9YR--`tM`R+4q2ix6+ns1*|KXV^MW%h*5VykU zsuN40Ksx9g!G4Jc=JeO%q@eXi&>dFg?4WYjLJVzO(7M5opLc}UD6$90YVRNZuMbIs z{i4t@sQ+NNj5~d#=kge>#(y; zsTs7$$cg`81D5u;fG5RiA>?7s-^lZ(%oEUAdspKu_q?&$!%S=u{y z59aBtvu?(b-3PtyBski=EE%oEwsl570FyDFGVZMI6A)(*Rw6c)&nR$-Q%c6kxUd%krFzbMLy%O?L) zpWnEu`ij?JKv)sMYv}vb+iw-RvF&NeAQ@wj4pRVEB+dHwHr`rJk}GDi#{JYcjRj`6JNmhh zlAj0D=zsa-qJ`}nfJ6GGdL~i!6;(n+U%>U8HfjM+E?+Zm)lh&dkUW!hx|W8 zr2pw)ExRuLln`g5Z8YP%I3!K2bcN-Re7oI+J*T6Lu2&(?5Mu5DOE({HpFjSej#p4h z{8$i+`2I6&h36}Vus+eW7WbU3lrlfw`vW)k&P!SMgtMC3_OI?d&pGwh;)q8t>nOV; zt?#+cm@~kDEuGYd1W{JNQea2Vac!Jkgatr=?F9xj>lL6jDjCK#$|i^f%Q2jL#75FL z@J}O#Q-ZS66Q5IETbyoN@ZtbbzEh^1JP}<%fmHgDi-_h{i^fuZbog}AXdwR!Iz#>^ zug=ceutlFMejZ@Q&S zBVY?M4|RM0cWe4`G_VCCUsll2-h`a;Ngv=|!Wb20z@S@Im9bBSQR@H@R61XxoO(g- zY*|RLGrf1Xoe+Pg``rX!K>dwJ34+6+6DI4IH9{nmffV+s^18LGwN^pp;Tw4$4P2li zLQD}XV^y<>j)MPqF_`VO?k>p6i$7YzdC_i{v+qi@FvF1$_09q5Fpn1@c();Z<>tQf6A*LZpV3cTaTF+7F4UE;Hmz#he)=BwIz&k zqxYt+_B!!SEUIV64)DPLF6)vS`)^>^w=!k&H@&_1<%-8Mo0L^sJZ)6wRWY(~7=QU( zcm|H+6k?q?S1@bK1%Iazbp~O5krzbZFnk=T16qF<+M9B1s>hjy`Y}k$1#ZpGk*wbR z9{tdT-kmf9s;72)srN4aCGZ2s6??SrD(df-^)JpSFrsw=3)8;G)SMc1O5%p4fCUv8 zkpIJqtQ(}Y{}EtCmd&k+%a;||=?h{}ZS+6#9@$vhKsOTe{*VG{pqOT;wgPfoCsp0X za2>1yYLxf0IAo7r=oyn5Ry@+8BZLUR`Ac5{zuY=|`af~`zZ+f4JRzW7`nGqwEb|Y) z(h)eerU%JQ^K2J|jh#V!T`0Oy9T4p9gC;4vlcv)#$NM*xTdG#Rm(i}RPCSQ#-uSJ@ zNWYr_WhSvx=HB1r?`?hEtB-uW^W&4Eq>Otqh<_1dl**U^{B=WHBMRmEEm@v^c2|_W z$1Hwhr*q)GbGVm2elo(Ib6HwhQIG!m+vEO}2#xL|t+d|dqfs?8ayOc7ht>TTn2M$I z?n`pPTPB1Y=|-C4nxj91im%_aYwxH~Z9FIKGroMWpkj?Q-0n9;TkeelR8{gvfVH3w zKzH}U?SHB!|6^F9p&EcUO$+yh{O5OqQue>RsXp&Yby1|s+E26JJgP}T=&22rkG|Pz zXzvIK_=ccBW%s+wM8!`@jHW&WYoM~D0vxJ!?NQIq5DW*SeaL4~Goh5Ji>oUtf;pZ~ zDhbXDFY^(OzZq0payA1V=pu3Vm zqI#zw?c(BCq_HWCb33R!lnZ2isb!4jtmCmKE>#5+$=NC4l|O4)zZi7o8Ca?P?ieR# zQmBemf$5aaVMt7@{&)RXleEt8U}wi{ep!aUy%qH96s-hFLUVQkdkk9wJHnhY_tIrZ zPU_fxXSnnGn*zL!&kAYn6#yP9SN!C!TmNbv{&=A^5(gWpQ8| zuaQqMdn%eVFmtac;yrjz;<>c|u`sjYc;|p-yX7;N z-$a4-Q2&Lm9SN#u=;6=xk5_h1Uc_}EFFQvQ37dr#AZh*Loxfk7>+o?po^dlU+-t#e zBF2sLNHEI_orN>^80SCAMjiA5u)d5QXrF0MFTadM85FO4P;nr3e-2;T~ zn`4try%wJvcp1rl?9-oC{NJzKfHe>pKGbNXu>9c^mP72zuwZro%)5f#5Zh&W zHHyigl;O-Wl0WI%s95z))K^tmGV5<0DXZpytUFvlFjhEd&P8sM@#=-%^rF|I+5 z2{Q&0IHE4%O3+O-fa>C>xiupceY9SIEE68wPosWQ45?TN5|~Nq>Wfm=@cb*pb^2p4 z?-+|9^git7r=zW*g}viN6ZJ==IShD@9CLc{#$rN9qby|7XPZnuvMujrqhjiw=SX^s z+UX5bV{lj-tgHaE?4f1^$1NUSvc0F%W1!{f`u}Qqn2!B#58z^bV4ES+%p(q0F9d`? zV`^BxX+n)*-br9yCqw8=qNx?vbfItD}u zDG?Qv?rs>mM7nF}&Vd1bAKZJNujk%#_V;(sUF-hwyMMVqGbL*N93x(Pp?@268@#qF=NQ+Fjl*MDRTXGoF839l!w z92%U&UgeW*DdpJkcs#n+1@#8j)&s|9v9VexyYtYQOL4%l{>)@}29r}N#8TLeAYnBB zDSIKjHkt)o|M>s;^}m9Xa=ppR4j`ZnDRv23J~+Y}9c{chE#%d^G&fX?{H1LoY5n1) zE3zd;m6ujl8M{($(Z0Lmua<`?(Z%vO33X@x^v z3e$b2LL9e7-8<-Q8lfLZ%K}ej3yQA=Oy-P9-p0bs#%A`MpC#XJZZ6x@kmnp7&EVvP zpXwmUGrx-i{3iBztp!5Lkzk0*{Fz*XqV9Tw0aH$+_bf5FF+|0Q6=?JjqDZacz4c`p zd0Co&&iwapU1k20m&KXCXMQrAr?_3h!npYq^RSd+_ufqgisEzZ#bm&YA~lX zRyectIAeGzIybb`bu%J$a-6Y`e%KnmG26k1ka-Aqho$sv-_(}?$T<6yNritfGj5li zv9$H3h~yW=Dhz&&;rKnK|MoqBtoJJe0G=K|%d%^l8u43>(9)m;hJE5oIYQ6IGKYQi zncc{?0*XCDP~?3JV&O`?<4#^94`S?D$jA6`?3mmQmCeYpi+2YU+F2K}%i|kh$k&_Q zh{$uCl{=<|7Ua>pvoVP?Co7@Zy|7n52kc!@ZJCiX!zQ`8Q2xagP7xu`J~Rt;i$%h{ zn}T_J*sCypyuidrdIg!y2?oG|KKq%W1?XKFN+hieWx0g3c3Dfbc~EGu5yMDTOWs2s z<7q*p+g|y;ToRn~UVv&)uRD7g zW}RoZym!X0m(tr@ML(;n^mgEN15j^*k2fY@;Am{`9Q9|Do>24$& z2L#}$29HFE9ubV<_9NwnQJiT&;~<5P;^aMQu>i{m{o<**ETu)c{U4?hMO!e5NYVK5Ko(a;|Xa!Fj+s2Q3TlM zmD?LWSN}JW0s{7tBuU_}rKTgu=#gRiZos78bg!WYp_*;yUAU_Nz>yG>^y3uSUV;62 z)%7bC9{aZ6yE@W=O}C@{)1UvvWBT>If4wZ0n?R10e4tne{E^Wbd%+bLJ$@6tC+K~8 zU@RhWtm<>0GsXEu<;ETtT|WqZVr*V}IJ|z-VEU-6JiZ)YK?s^6BYL+0V+9^N@AV1V zozP!ZYeCK}es|oi9G%Mz@vUzGPOeJ8pkv>iQAX4)^BO*bH&N<-F%sbfOm{k$biH4> zU9|sP4<|8s+6$Jcap*wg0&`32{kssGZEOLy;ZycO+LR;YS7~=EzmdL#lTX3PXQo!! zslv^kJIuli&Of8P$&l%y!~t$RqAM`gos}U0W#2bb_6~)V{7x&x^v&pNlCCHb0_*df zc+7+F*Xm2ZzI#cEbeWfYK3C>9!d4Q67thzH?_dY7)GtHLl2~b@}gkZ zaDEFp|2n$AUn4gG9t-#JWOHTns3lM`PKnK&E+@6I+)n-VsXHMJPO0bJMQ!1RFEsRG z>JwR5Z>oL-NIxF3YW34@Gu}(OGm@2x@!$msbTULl$ad;9p6& z^)E8j#=B{(ByLfnUb zW6$V?*F~CNSPgFmq~dS%;PyrDZ1#onIGmnGhmtzv;$J#E*X`HBea`f7x21R#E(WGx zv=>VG2)7h@MnaI(oM*GaPblQ*YZO#)Cll+Q2zWQd8osU&MCwQ+CYbd;LxfgnH#fgX zN_Rns3UC7o2{+^dy1HHK{c^`6BW7z2cmS|uzZUC(+ZD!@`!dc4@1DXxo+h4OYjA_% z<`4>NP$(2?SB0Rr4kqtqJsgF#UxblOWukudMx4SGeQ+4uA&WH3Zb#3b` zJsOD(wZIdwecPvlGgoAny*9?npIo&zM(G_(L5z8PGU#2^&SWvC&YRXb&+`(!ZTi3} zS0iU5ZKbPd>b)jyF(SwLV0oP+M94EqeRh*d)-K;KlK$0C>&}K=U7do~k&5o*;|0#r z-B~s#<7JO}!GlF5TkrP8P*wOri?wxr7q84kOF&5@Ig1$Xw09G5IF&2P1wcrIPJx6z zlMAq}t@X{^L+-UcjcC;biFA+OnjkGFm#sCr7IxIV*TN3QB8n4Sr@cLL(j){>Q$q7U zr@53Ijk0o*2^pPjWp*Ap@3aIJloCLk5)8$P;p5S1Ql|wInD*{`zWwiimX#^p1aSZ+ zB=4d*L|5ydQ_HWTu05U}tlcw_WEgA^WI32;yxK@8&dvtRyadm{!t)}OdFv`*r$T9r zWuJY}%OmkN`hbG_k`Dso+}BG$i&qRWRF^&~yxdxC`V~{Y-n=SJlB)n)ThaCM zN`E1bc8H z%P;Ms1r(J(&ucr8^NGZ(U34+{olv#Xe!1s)LWhin;BEr$gD7g;=aK;~#Jr4_BBN_d z-lN^oh2tnPvz0MX`1p$YBYlG;gXr}DGRMsTFj+B(zOXTf;0Ekn;>_quduCl^KB?zp z#>Ey34!)Kg6$z6>kdz~=g3dN}$_lsZ*DQJOZh3(UOIaur-rrCC*Vp^cziIF%Qfb-b zdI)@D|53#o!0{FNHQdb3*O)#}b&IFb&`O^K%sH28x4rEuH_Ie~c;Em>l<$hO)>&dX zSfzQ58Ghn3tXV)>W07TJBzBr@$g_OD=WlHKkpAkQO}I`(QYRmBH9HNk`;eUL^aZrM zkzDOWYbS#xb>K&t!&oHu$Q0z^F-cMZebSJ*^ z3nYN)@t$I@?9P{RMFx+gohN}(wN|PCA1?rF1B3zPottjOlR#e8BD;U4q*ed4i?_Cf3H_~)yXalO^g;eF zz$*0F1#+Z|+~;60$_|jokUA)E1l;p@1fRY86@1JY{2CdxEQX}IzX9C?S;eaGER>}v z(^rw=3|BH-DX12L?i5RlhXh3of>S>3%QDx3Pgj&wWs^V>j`YM5R{SgiOA}hdfptBa z@hSD_M79lpPw#X2i007ob!Po;3?pDsZX(kjnrmm?rie$+M;S1GH>8QXbCfnO^5aLy z>~BzD(7QryVhW)*y;WLCph|~W zX(P6vOif+;;z$-IV!Xi-GY`nwgiN;JTu=Xpqoly&AodHb@@PH%(Qxa=6_7@2m|}GO zy;mGWdVRa9s1NWW-0QLPhQ#QO?wj-7>jNsOUX+py#u$z4m|1gY)MA0{H1459>dj*W zK9S%6r^vld^Z?kk=pQ%raDlDt;oHCwT0xwF2E<@nTgDUfUMM>QJ5U%*;{I5-@BB~+ z0=H<#bgtqykozn3>4+7hOHCu0#T|h~p*P{8(#Q?*jhsKRq2axHK=%6Xc~~Ma%RC&k znH9=PB}coA(ykVha~>S8(%u7{ltcWxBbZDSAtSLK5$Q4?o2o51qKv2%7|+~(A0gOkbTd^(OvQv!#hl2XXGz?*VsnZV&<%iLp;q+ge2}y?2E`$ z(w|UL3Ce)qif@D{OCO%<@Liyk2>Wu9D>v)2x}w_so=GeebAXD`k@*p&us^Tm^OB?^ zYzlSzemN}Gkn>q0ey~3dBBkv8rmr~%r)xLLAy>8DXFVXYR8Z)EPV1)PM_P9mz;GHp zkVqv1g~@iWv^SC@&PV}WFucbu7uiHEVZipF7~p|y%vbq+K@tBpw)wx$GAZ!l7=YHM zs9W+o^-UnufpE^Y%k9R{PgK`F&qyu3b3`8ZZ4}d2!5a77`1*VriN)tUmQ%091S+Bc zgAKLhBA{zFvSKgTI&kUKh9noMPAvg&4-G=dI{2GCvNur@Kzc3jkT7$Bmi2^RlE zBN{aLF*%~3hCUX>1nAu0X~iOBy8iFhPfk@qQ8%5!nimowGcqzH^a z7q%(_|RVb+ZPGy-|wX?N8I**Z)&$Q%Fj zD+aQQFEIX&3(S0X6L`e*WPb6r2d%%5$uDKjI}lx=oGFsY+Nx#82KjgU+)+Z+huV;f zkFfQLnaz_21qZCxselaCjCB?M3|^Ic>N8I^G>wTzvL%lx98k*U*!26lMLuKfU0({F zL)RXS|6pwJsNX#(gfx^M9g)69Bqxk2`~t=C`~`}O{|glNKLx^JDa#DozyTnfX9xz_ zN+l+P}GB zv=0HG5V6UPC%}X3BEV7gT8r(HF1B!K?M?N5iXLsd@QMipIkwlwM}o&IiqUJDbuZu^ z7y07S0KDi8PbUyiztZD)=TlVG+9HHty^~VQ~PAkphsaQ72F+vg8c2IXuf{2mv)%;$)1I`sY8C)<07-_G_3kZ_DMMs*a zp!#iJ7RxCv{e(c9@&9`~8J3^1-bO?=xodkjxn1U4aWe{VXG9-if5m7Fd7`Itj_waa zhSE~_RT&jsj@}j0H1~3OnvLn!Pt!DHa~^!!{_iSo*8VH%JwZglRajRilANdPdQT3U zJvRqz6M4ubcVy8ss_-H`m%|O? z!!K(zwK@pw1nU4PI)KtPG8-r6mC1*-SCb~Zyq_Z$9=ySmR8!g)zJST}{W6jrAGf?th(c12o3oFn4&vaL70D@HTuUhbVV z6f6rXt`ulO^jD?`1z;5KH%+Qs6`wJkhhzX#idYb6L*si!5272>U1wog+G$s_zE$dH zZg5lUKJCAIn-o{08oVfOe;~agDMXDfHRCzAYMe9=0kD;pw006Oq=*zT=?1Lf^=g@3 zCTR`>_A}4ix5Sp;nMO&~L4f~*KN~)Gde*D%xy~hf?VIFA7NjxtJ#FLS{fzmCB4GH@J$giC{iXkSK`FwIfuZtrztVNMPdtaF z7XAL!z)Gkrl^hPXD9P}iZ4jWJG&e;r{`kDXO4{Ck*+QENBu}ssEjFN#6MC&tmrkge zP-85WS@Jg+l->Sogo=l`7lrsn#1DnSsa~>3t~cOun`5b{BndkM^M8N; zt|pwckvmuS&zcB;rTwwA(|nnc14JPGRiDbkVaG=N<@oHpsw!Q(s@3kI{a)4k2tDgu zTkl_t@!IY4rHeBu5uTyTg1pM5MnVv`9eiImPMGM0-LD4kTWIhMu^LE{xu8w^trFZCBB0y#UvxJ=4aY3`pnnR%eVr6NQ zy@4*3q1L5Q`Go(r_lk%wL>Bo5AgTyv9_8px^uAWJ;ORs@pfaoZNqg6x_8IiBd&T2* zrxx)IA#3NgL^in_dKLgJC8kui?6lWj%?@rhs+CKvVjpyEyoMVHuw@06t0o`>h8px*O= zTwVfj?J71`fY*svx&W;kVqfM4 z`4CO#$!!jm3KTj$jR4nIZcrNKMqJ={9PIS&{9N9=mlryP3K&rmE2Xb4s`Z>@GZ859 zi;rmifI$h>pZ$ct6l!c!re0Fy85S;_W6nu;4d4+4d}Z1#iHw_ZO!b_Kg5H%|JFzwc zW3arXP;#~aPEPP-R1Tirj$A+}PYgzM#_M0Y#A#5`xf>+5kjGhyih{Qwwm7d+k5J6 zNml@b%U9X3<|$X7fnBHNsh9t0a|{G3VE*xcIlB^I(EjBSJM1cFisNwQRmhLQt8Kat zT!vT4KH65TesvspL>EtZ(YgELuT4Dxqt;RGV?A8}Vn3SPB=Se^fxeT59ee1^!_9j} z$T$Y(vhL)eg=Q5hqFSt#1m+yJ;gh81ggjvN&uYq_WGJ&Y=wk#QUMq<2?z>rgwDWTl z9zxy+o$hp%EOvA%xj;7N^(^0<5K#ANC?vQcYhhCKL}VdoETjh9oy!cqQ5G$HGnNqd z@C=*(!auYY222_mDg|i1uV3Oj78w;d)YJ@&=r{%el!Z|BqiyNdImaQoj*E@6%6fk=U#;W3~dmR$UQ zF2D)6F)LU2dv9Me2E2b=pL-EkFhXgU@R4DM<5`XE|Krh2R}Y5=Af8wb5P^O>hi{Qs z`C1ZZXPMpKtCRH!Nx6?W?aBJ=^~a(_5yufT281@E8JXyZO~8x<7>$hu3c&#Lc9EdM zT>%+~J4@{Ve?OJLx@mf{_x~G+QvQyBjOTv2q(?|_)Fg@fVqOn|S<-Wls=miCK81{- z1YkHB)<%)81IU~{0D>NP>)$Nt=)-{KBxpY7)o)?ttKe@CC8>+_sOq)ZBbwehshQ>O zDJQ^6U9@_cWacYz-(GeWB)Ha_Xsu8Q$?EC@Gm&2e?1^3Ysc;2lYH^V2!P7Osh8Dn6 zdYRg=xC4J%&W9~^f&5}Ie4x~yDa-Bjs;rG&fwNR#T&lxbWqFe`8@RFXM?L@LSAhS4X^-f43!8wPfq{igh0pv|pcqt5^k-d{YT4s$i zObdqdW>gv1Z2&f*jHmNSz6KEBm|_{V?%zoN?{dvwi3bE+jaA&+UBCU9iZLD#CxNER z9avKiH&%+D;~x<4E2NfBxXJD-dZhx~6wJ7sB=kL{@|vBz0D0LCs%^R|;ZWh(hHE6K znI4t@iEVd>9bQeoUoP--&`nllJqx-#fgI4d9427#A(9BPHZ$eGiHwhsn}bq2Qgu@> z2D>Su6ld|7Qt5rI2ZIDoH%o;$1$G*rfh-6d4@ST5&zbNgjK57oQ4ukN&yQLJEzGMc zz&RCVw8tj7Hh^jxVDbEVpM8);ck^o4bT!8ZMA2^jGfs__=->*dCM{d^YfALzc{ETf z8hHW`rww3FTW;&{e=bDnKb2sA-35w-rYmroJ1%iPg}*#uu}@ZZBQ2z07`w^O15h z4*w-K@Xxq33cOPQn5GP3)&5OK%INylY-SZ$&esWL+wP8w+vW#2x%953tUj65t7CQ< zqjYU&JKO_%SD=Iyk|)UN5c_bWMXe>dLYkS z78?|0tmM9?T&#o+4ny?YOW66WO|&u#V-$vwA1tV0hl3+ygUZ4Q%&Vy`Ri^#i z7aljY0KG+u>;GoL@LzxL{^RRxwnlR*sS}zjjI%klQu_e+NK)uZf3)R8v4@>qBW>z5 zwuQ_4&6IO0!X~;~pc;=2%8z^vm@AlNf!_{0T`?TR$s*8gn&yWdP&few4P?`)xb8MP z=JdoV=tFfo66-<0Do-Fb3FPFv_LyX$LXKD@k5P8|?UBDcZV0(0DYUbBkr8O@Fa0zpaXu+5TmCo| za87q3JKkgsP$K4}$1yh2Fr`O$sYFGx#1Na^7CR4-4T}SFcAO9rDQhrSNPGHlUQZ}_ zgGn&r2x(#iw>HVum&o8OKSBxx9ew~wxbJl>kjH>n3?ci~iX&wEjLonXi<}p2$m-3y zwn9KHBpdqA0Oc4~BlYIT(D*k`Kk#snxm+EEs0!7+->!x-*MF2mXFpH#vKM+A#l^CN zUtjD-jE2^d3s&zhC~eaVn#>#d<_I? zt&gx6$XOE6k%-iawQkKv%%l(q95#Mp!!tbINuUq~cNEGvPTrQ2g9asjVq3TmitZ&; zP)isK5jYsEODUA&D_A=)Ka#VwhVNTir)t>NXk@02DN?PsvQ!ETob_WUzCnTt0+V$; zx=fpO7=#3RS^)(_KuQCj?Vn0P#Q@)@q#3)~$z_&h38E$5!;Q}K{7*msRt<6IUX5h- zzU#~!0?$Lf9&7omn`|Vt&1?Od<70_~6|H8W2`-}u0FKu?IwoYqWRa!coHWBixLNWD zLFOOa&4*xOC>GevH}po9e6_l4f-mV<)&88JIuzLT`WI#%r3# zk4Snl%IfHD;hg6k{qrY|TD<-~0Vs-Qy`dK%XJV#;BiUQtgeZ_%IG=pTF9JZSG>@SS zZ;yWFrJ%jtyGf4jtU}<<(y)11l$lLWi|xKCrz;ofxG^x{{NJXpfrTUbVTDOw#!Ti} zkF^O0Lz^GO{HuXtEhzbgm@z1=lNomKVoq)p675=sPB(f;mS@>N+m7T)T^gh_0%5tH z@-qe}B#$kGGZ{$Ck1`sO%Q6ybC#AvGxF!y%6VfV1sglMp-6yl*QLyotG;e|R0G=A~ zFz`jbKcPbio59a>sHDXA6vm%-RkXJ(T$3jeQDF)TPME^7c7H4dM?u)vO67uW&g{oT zWr=+S#({)LSvPv^1{m4WAS?(SPZ*BUXH+oIHRV@m@sH@h6_Jt7tvhT*lGE&gWgJak zE97j^$X9P&d|E*DRDa|B=h0sT9(K}AMU(2=&>v6%hpe-lfYlb`50C#NW&m?k9ZCT0 zYOaxM=gNHivr()%1E4;6)I@@ktIJ8bQ-KK?X!)r3;NIPMyTeyg>q=_*e6AGMj%Db% zNM*PeenFsCk`e%NW_jJF<6}0iQMRucoO~K02dIPq^1%HHz2^d@ulhb1OPxf0AQ87} zlO|kHOAc(Tre$)Lrf@%4OSmK3WLr@&N|YdWfFCi3!DErwXACHX4=ckzJSzDHr4ydo z#=?@tdh$^2Wm|Q*#um#Y`R-3^JF>-=J9S`i)@ZVS4bV$5v)KW?AR~$(4%>e3zo(|L z5=Bs79f(zZ#pcz4g#7mhQb9nQ^zd%VFECKwzraB00S8WzWb-6x9Q0Y6BC=5k2Q7u1 z3C!uigw@El65QOt;a0GjknQd z7U7`*lMrSVPO`yZr_WbBc**_5K5hWVXRK|VTMA%`^0Y2a8*rBZK}B9bi&MRF1<+aQ z1Ee<46En1M*W#IM$a_Gjvvks_PwHyd>PeJ*C8)xSBJY*7ju8kFjWpUl?wcyT^88C# zN`$0p!+nN?*>`Rxc5oT)7VTvzNmWg{jOZ_AD}f{gGX->PN?R>{?b~YjL@xmeO=7qd z^0E5NrZC}_p$6a@>e$BYb~Vn2t1wRQ6f(Y}tJ?`o*}g&@0nGf1g!vXrXCm%3ua=ct zPg4&S8GG*nIx}w^0RyjhTyUSm#XEm2vhovLjbz6%t9tVS01@A`qDxa$#59W9{S2>v z!rF*kk>>D&V8MPIEp%6yS7`bCd%NDK$8M{?W{yT;yt~Qa|PErZX;P$APK2D-FFKsggH=|?hdt)7MHcVHgDpp@oD*g zk}8(m4I#zy|I%HB-yvML(~UZmo99}EFy~5v+e4d4M(fVL6-hBm7P3Ke8^@SXn!Pz` z;6(^RdIK!;yHf6zRM|gv50tlnd`Z+pSFL@OFCG8Pm!PN^4SjQ+L}BZ+qN$^a_@(&c zk+WuYhc2G-cYsG&X|mHay7^|!rT6wdKs3MtkbRfvN)NtJ>I+hghMZ3oS0Cp49A@u* z$*B7=7#WYw9AVL7sb7fFPcV&=u1cB?~;~on%-(EVKlbCUNXw#ybJ9!21V+UP4uH0313NDPeOT^89_kkcztQ6-T z;ZZ&$6u7`R{16l+0?0(n;D0ylj|Ca-uMZ;GWvrdaFbA9$_AnYZhY1TB^HGI=5J%Ph z@3m3K4*;4 z9hs9hG#Ir|z181}{&@zX=ns?t?1lO_z?U*!%h(*R3QOkdj=$e!<&dR(a{bdSR5Ct3 zmBpQe!!Syx+aZ4-lbtjks@&yxsggihOfOsX$(J<@d*}4>{AkN@~u ztMY@eRmBIc{N$C(?aPb4+M_kDtx(RiXJ#fv%+JyWmL7~i&kqB}+|)CxE^zarmA|w- z(r$2^u7p%8Mlx2X!3aMn^R2&HEsdjL<;`-pT2VXR3(@QnkJ-u+VpxDVm)fgqII1Bp2(Ki|Ay) zLthxNy}!c~@rY8Dcrfb(s@6~>VDsW5Kb_ji<6p?G*ZVkXXc}@x_sCS9Lf#GpbKlcrE$7r^&Pss(hREV}K}U2S5+7kDBxo)}bl7&iI>S5h>-FmNW zcy5_S}USh zr=LCqEirZ~2jqtEKn7IO7cKn_x=Hib8($-TeTE*#ob}_UAVC${?9MQr>xA`NrN%m< zDwj`UtcJd?_DXC^mXBLy2ijb2`&>GKd@zNK7ftpeo53Ju*+z(!&3K8+I<3t2;9GHY z!S%DL9!G&UT*t^VMP{H>ZtiDC>CIgQnW>KKGoG1m49z*hi+q6rNp-~`#`+TEbJ7Ty z%H#`nagUUbc|M(FfTYlD1W5{xM#6;LB3`}@*k>-ELP?*)R4UPN7P!CenZeaP`^=o; z=m;r?@1txx7dy7b*Yo&R*!$u>dynhHJ;Mu=C}5h?l<6>}F0jvgmWvJ9kG8sg7w@^Z zZ|{7JaegtZLoAK1kLcbrJ!k)C5X_1>I|%)piekAfiYY9R*;-A~Bn)9{slT!SgWtrw zPor!~tr*Q*=c~Ef!6t#`l6GuuGWdmd<1y?*DdA;tQwFpaveJHibgzt-m(kBZl^Dbu zCPZJT9BYDf^zDy)0Y%}2P|9bz2x!6=!@>;^t1)TAoWBrndlbRMx5ymPkK?BS-}3FT z*>Zo7wv4E3Cwcx!dy9In{tR7#Bov=p)~uw2P4@j3TQJMoO<}8-TR+4~W}i+drNK1v zav5Hd`Q?^PPBJ?z#@JRa9jHEkUc;;3%Pq1q?N#b*kz?X zUO)Nl;%r;L9*%pza31!(UTnyr^Uy|?a^(?rT6kE-dI5Y{HPik&V8%9ZP3DR zxj^LV|2uL~ua>B*x-vp5j7J3xs~AsPsFrBsN@Oq;<%7M|cxfK{s7kKVw(>T0|bm!+jaZ;rl)jy)+%3Tic8mXmw2~49n_@kFVDBmpJxaqn!nS=R#w#N zq((B^ZBWXa%fC`vnm-Zw^pUZ^D5$|ik=64}|HFD}@%${cfPF80fNZ`S5(N;6*5|}x zl@Rrd5kH>$HZd{sx<4)xKaQ6XUxK`OXXxlSPk2wpse|FYp=)<&K-6R>BBEJ(FC+ZP z`jB`(TSkyyMT;8mQanQQ&U)iBfp@y-!me;4HKHH$B_2H7an^%xE&mdC6l8Y{p-lOn z`c6q;kvwbXgU}5n0Kj*p?s!zR|7kJ)kw{Ps4&7e%NlWn^IKz%6Y(tx|EG+vxJ>XrM zn6+;Sun2%MboBAjizR3tYkFT2r8gcCu_4|Jya;R{o3L3R12j{Hz7IUk3ZXsW1xa$c zOxMVG@3!pO0-zSszLj^WWyyVLNE?~Yu6Za_50A)s-7`iv+n|;ru_=9b2EKlATwg!g z901}G(Pg`?b$YbPEWR7PQR_D2LN0w^|F&E)a;;8n$+5g<`v>k*dYNfAL-cgH^?7Tm zThd3fh`&Qn!6Iva-(~$XMwdDdar*`=k;7_BF}^&X%euTEyXIc&2DtZGAUP)YsYCBh zV)~z-k|iU`%3>oNY&9`yr40a^0@x z{ov6;8#bU2x#FUE@F>64LA3TRvDULSjA6M$SDfvSme8%v?7WU^X|TwZkiksVv{`uD zW_h)$%-OAFHI0V1LN7zpX$o$uy}VZ}dJ&oRgH%UQ!HANbf~G)a_uEguKqoKWlt$~t zUpU0eonIP{PJqT^;+4kZ2W13_Ax-#WfN8I&pZXMt4tjb34)FyX`tXOblHT4EKEqr( zpCtf@cvs9+&u1>v4*(*%hV>=GB0BR=-U`rdk6}Wv-ru3keY#FSq5ezek@agMWy2^D zwflzI`WO~ByI7&=OO~Fc*dv5@!GI1!^@pB1*JV^-b`7C#XP8%9Ji zMMUpelbccv5VCkH31SEJ4eRSwv$?b z36BVexG4#?LIVXq9`K7%hr(=yDZ*73T4}D8P?fb<)LG%xwb&6mNu#NkQlXtRk9Ym= zAHN^!8+P+PgGEfGZsDW3k3+R!72@DknL9teWjeb8c0jkF@q8ZoPMe2fcD45qRbu?KzrQrXu+ZAHfB+DCb!gPqgu?BcQnFX({2=yhrp%0RlZ@mER)hWY^LcOH{f}W(xr(ny~`hzN+JrQAx?t}<@73*B>kxQEg#B7-= ziEBvJgm(gx%hW(Y@<%^FlB#AHp}f4T7B4Uh}A)s;FIlS2y4p?i0tP^L;7Gt;_~FOAO9=)67`=@jy6u$C$Du6m z7ESM{SnP<5baJ!M^4pZ&^#O;+K}=p_{GH^wCZ+*QKibFYFbs)Y^-${I82V9T+@3W;L5gOJY8zT@rvuM3eN#}WPd5hGISFev=94=!Fe^JTlD*rlXiB;3k92Bik@9SQ zcf_2WedCYPUn_j7bd3PzVW!QfbcF&*jzc^Vy zY`I?645-*qlz?&~xk(9p!JWyj=;C(nFpHY=rh$y%&Uab%J*iP2? zOuI-gbJi`i6*$MYv?9AJp~I1H(~Tt%6E%8pR&A{f&LAinnqfByh7v6dm;izdVI zSqVduy_IOTt%+SKNUjsC4 z9Tp-^EiW8O$)yg|Lj6A-4%=+>il1#5T@J+?Rwdci31`@)N!Q9_({~25xHp*JXBxNa z>N$^hK#*Q!$ec7xdClPZ^&Kr}UEYb}39oMvFxxQ_8KjeBp04vo)@xp$NcG%%B644W zGWY^~_n>0sQ{I!RimWX%pS-=*Wz!q_AD(Lt8cHGcs}CP%PX#gNY>%Ivm0UUf{pd** z?vgrs{aon?dR(uW-z87y4PWk+sb5j?leHw#*n1@5Rc536X`=4NvlQ{xDesY~C+Ubq z@J*cek3Lr5Dl374FI%z>P7(EYN#?p)K1E@fkRezMoccJx$s=8<^y2mLwa{;-vo(gh ziy5Z`J?4nqy%>;5p7^!MMRh1m`Q37pkIQd)cH4tl#GDi#7kwI&uU%{jdeHf|YH4d> zj5V`dVjnqvZ=1-`b0(fRG0FIld14zC*OitGTs)X*^w@1WS3nQ51L2VjnsAR|xm72rGd@?;2T};d3{bl$5ac zU+gHH?;E<2b$w}8G&BtP6t+`zBulA0Vyhq96kk5gv`Qeer=`vNwF}qp2H#fw9^aN- zCuUw+cje^iky4%T+LYT&QqKcEv>)Ndvd(rPWQX~pX@4sbV%W|VCC2b)-v_jRvO{eB z8A|0lJY0eXKAWYs8|74;D$kTE;OD17N>BGT^MObhzxG4Z0!#ho-yagY1vGw4J=v4p zSB+ogUyt)z%*u)V_-bZw?pN{{uIfu@@Zc`QI`O)e>x2tuIRLQaK#W*0*I1w^mAG;W z`O>Uc9RaNySO=0zY zgj@CbE!$q()A7N@Z^Yo+Q|exv&xFKpr?}8UKazW=_edR2=UWQ8u8mIH)GItEOaneJ zQ!8WT{Hd#@rInGdR#b8lckW3#VM^aZ{VRUZ=_1vat>O#tkIN(K@4kW0l$kN-2%qI2 z$DkZT&)rAmhSjdd*{f%1%Gg~%4A}sWIS=l5b!^|Gx8IJ}janGL^+DRZ$_HwH8NqCC zW}3mK_8c#aSZk$0u+^~sG~98Mu^vQrp|=?|S1Zi_Q~eO;NltJu%v z7qovVn%rp!uU(Pdk+;=X6Y6e7K+RVcTkqWY$lD+Ds?V47O{B@|hr9*?3E}Sy3rn6Y zMe7GD;(v=)?@S0RB%;l3wVc~!k|3fmNfkCC>@YC8HHc)O(ZphF4#wz(g{RDS0)nwX zqx;95)o8iV$6rq{IQMy77Zo_9ssb<9PM_@Rp|y24GBIK4u=WMa$-Q_;tjn&|*C*NF z3rcw*$Sn~;yf}Bf`R}OAATk#|guC#v@b-|aYWX(&4Btyf*uZmjiNV`aseJ#LL{;gq z4i`+ppH8P3(&xC127Ab0M5QuRzm~a{+??!a)3zxcY6{N`U1+NYoc6RkeS$a34|AoG z0_nVJJ}xE^B!2&@EauthI$_H^zCV-&=zV|uY_miNig1n1gvEDALbM-E=z^^(qO2z? zp1;-{bIt!m#;J}G0Zn@yr$oon#oL{N!o?tB8ph~la7?`d-$Vq<+yU0LB_uyX^?CyrRd@;?i2voWMNXOy{xOg^b77JDN^{EDvKl1{J);@t^&rSuszXuWaP~X*RyiswAL3D z88{Kn*b4T&JM3Ex{yL)4``yL1!y9PF^#y)fA0Z-GvI=Qh~0)ms&z?ocE-3%gs=tbW^kz^~A~A18K}+r9WOfc-BlH4dM#^<)3eP8Rr@J8VII*wlc+76* z^8yAq=B?A?^=*qwNzTyE^^uRBH{96rIkvraBm~3m*qLjd@@hro%^yd9dQZ!vBRc$* z_vNpsK+O2*^-`&(;ck53W@6gkrNtbwEy%r#)G0m0=N8KG zZ8bG6dBa}tq>FZ|r8lMd{vcnCqy8!UY-=<79h%Cq?hLvA#ekm$KH6wIv029VyftUE zMh!(_*56DX?+G@;G`$^$yPOxRxlNnL_lLc>h^UGFqJI~kqvr8e|F>8ll-WSi`_U+k zl;bI{H065!IMSIvM0Y+0{PD6=xlP>C#{F38_>>O0f2Y@@JF^zxXfRWZ2UsY}Q}%l- z>qkFLX+ReZM8Mp2IuGkpAQowUEc&Z8tyzKRy~YXi)SJE9tMP?p1b|qsXd7 zT3s;ONj0s1KOoFZypilZ%+L|Ml~^|yx2Yi5+PN$&8_MhaYSKh!y=>smj{D;pB~X12 zL3(*IXml5Y)Oj_kMuIGPUnfRVh#o}aPUTf^vV1kIJ87D7T;F{0%Cq}d#U00M6iQ5o zgzraIUj!uy<}GX#pRcb1&14lNU;SbbVBkGlOjE#Aa9NkWP$-^1>AIf#{=!io8E8&% zJ^0QS-@v|8wSAl6{?heU+ozZ42h=j-L3s>qEN*(y`yQLQ<&*PB^A-D!jO-&m-nu5y zb7}6a`|`)(*?Bdc=ND~hY_^z;{ZP(|xFqP?kRSLYp|@H;6KfeT>=yY@V+!8AcS^Dw z|H16mbS=F<$d+$8%}35(321V55pV4nVMxT~u)b5z;TH^L%Qk3LeE;WQA3?Q7_4ZAc z!Is;D-jbIXC%xvNNTy|KVXL1+Q^uahYfE4IPVSxT2Y3nC&-(TYyt(x*JHL{5z}$4+ zi2y1{gJNAK&RRb8|L$`0fzjHJv@h#+?A}ptSBv*4Sep@01jX9uoPBw7lmr3!)+Nvo zEI9O|FPdcAn7ltvSgq$$$(>$6yzE#^W!Z+N+@=$EpDQ_(U)+o3toGB?d%q}%q{!mN zX=T=mI&k1P2{+t1VX9k+F3f6Y=YjNUOM-VOoI;8{Ld3*-cj>4Rd;5d8=_DWBiQb^m z62Q z4?oIB`(iQiqUK8(_Cl1%^CQy(KJ$Ix!Bd&NhJ|Vgj>Z=MoV>z@YI!qT%B#pb#ox!_ zx)VL@pJIEzGMY=KX0Oe|458xK7nB@v!X<-$kHYEkQA7@dZtEaR^gvs#@p{=A`G+m? zp-2+HJIvT&%C6JqBL&?D;VJSt=bw8oT^P%uiElnpECS(shmT*A`+11GLkF{cbzE6MKRpe%g2*OjmKDbTEJ*sXf2DC1INS1#11 zjP_kkXyrxKx|3(i3$4$WF?L>E=DEIK1UK&leu@0MZ}nwU6UFNlTUHtQ+)>3go8mUP zC5=7^k7z`*o)mTQ5$DIAAYYen#lI`+I$~o8dhbEnUwu4pDWGo^!0~I?)j*LYi1+fR zn)x-Zg!V9=`$Sp{sH?}vz?%4-8fTY{PhWJacH8d-7=0aB%0ak{<^)7*J@r`(IMplq z>kaEavp@DEt>KU!l(`w`PhC}_=i>5wIY9-V`>cGTZL<)PWYo33J%KR|xIe)lUYlBk zYBGLu&uGlDZBy@z3}!qd8w_3n-~^Izu;)a2YK&lZ+zGp($pC^toW?-qCa*Wa_WFhjL29U&Q-c0)YF1Uu08#p{kBa& zA2xt*yLo!3z{yY*Yqp*wc z$qtI{IchgK&uzHFaN<&>yl%H@n%Bw@W-9Y-COWS@nYn~+_n4BbX!tBVEpM54b^^>x zi16hlH9~C_^s8M}o&U^<6}VlL(!FLr&l{T1;=h%jD^{C z#XP*;#1q`^g5=Y6l#4i#>r~}lQV7=Dm0}Dtd}nyE(wZHGW$3PWphH(f1mcZ>Wr_GM z`%_b~FnB%|Gw+iuBUNk_%zac;qv-!KZXW--C{Oa}51~)XU!NfLSOo-?IHlNcN8|Cb zmqkNkT4j-^Mp`+V0Dtav?#+nt;eJ%w$GZ}hK(So0kal?HX$vr9k_vL=-_lCvsu1=*|K*=c0}3Z7@;DP zMD|RgVegg9BZtgv36(vw3CGUNjBxDDvCiSp?>gPR-}n9gj?er1$M26G9&UH{<35+` zx?ZpA`Ff81vH-%xil)&O*oa*@*_YrK%H7<)56!LLT>G^-qBFDQF-?a;`qRE|w8d;K zI9YerZ4@_`HB??X*h}4X9re7}CeCZCA3M@tdC2ZVxPxNhS$mmaFZwRp*5zMlGB{xJ zczf1Co`gr~9y4)btDv+mnUij@xlXc6#e<5NCb-XR;Z&8}PyG5K`ClOPXM9{bi3hjK z<=+~Xsj+9-It_s?xB|xp5k>s(3Dit}rY~ZzR6qTC0)tyR1g0xz+ggz+U*j`UOV`-ep-O%??P{F`05(zgg? zTu-Gp3SHigf~oreQnYq)_-4}r#g94)Ho1^uAwTb9_fA$HEl;g`brrf{M`5*W9#aA+ zQ?Zv8+Hli7nYJYJ6Ai>qU*oq@G$T1a_px~+b>_{9GYgMzerS|mSn zeeA_g`F0j$mxUDao5vmaPq!7end~qQ_Ql!L&nSOgeP7J`$j{4Q2KxT{7e>jc(poum zyk0BR=ar3vDI)~359SclRpT=;g;&g0HcA@T*?oMUyr*d+J%EZd3>Ix>D)*$uvXnF{ zdd&-_9^Bf~;+!%TAtew_cqaO;V{G{!1?!>Q?Tg}2p_eo`%ZZG56|G9Eg0Bk_gs2-Etbys0adAZpIZs}h`5rt zz5xEJcDY^ZdyVA9eYKMNxZKLsZEIkF?PRy%+sw$wP&ycE)%Hg6j^fG(Mk_Ef%Auo= zRyaoiGkiD!75e7Y4NXV7HwI?7nuX2hQw}o|WU!S8mkyXi-C76BjDb1q&FB)6<#}n7 zvCmf3j9|fcBwLGtv(SSA`X`7o>z)OTFVg_gY2K?;fY}f;0m1Zm?pO*#6 ztu5+ZVrjzHrGEwzdm>Ugan^d@ufK0U?M3Y=@u6mKM>?-o`lFW`)7cr0KzLIY<6fWZiX@*kY$h} zl!%NI?Jw~)V?{nSeJd30OeH!3!VKayeoJ-Th&Can0rP5{c?j{RZ0(lXWW*Ulyh|$d z`(<~vePv&2YcoZ;Ua0g`r+f9JoxDC7@eLP;;fR@_SV^C~$QaF&#K8^cL)sNfcuB@M zJKS9@TgkI5p>7=GF!$x0+lodBrVL{Ho!sclv*X1dzpER)`S7{#26gQppyiYv%G#0W4{d_#@GoY|LmlY*;@QP%q@pw9}?3=X- zyw5uD=Qxir=-v1Q({@&Kr zq!2uM{V_eKOdX$Y@gT|Ezb7J#ZvRt}iHQ4C#uIK4^Bpt7F>*sa?6n2utFdDMW_ytXG{JKF0av1Nk zwqzjK5Ly#>Wec+!qcP3GJ*y2R6*o^IF+_Aunz##;-~|rc;49wT1?umuuWTrebI3g zY@MFx2tEX}f>)X*#QS}vmd{a8Q6hA1k{M!bmu(QjDA6W{$nf9C!Tb0O3eE_Ykpx!ID zjM^%twj{NG6WRSHXxRTGmpXX@+R4tYQk-lQ<{C9@DP+biBXBKfu8L26x$th;jd4Xp zW4F3fLXxiRKqzLSE80*fYq#=!GxL|{^}n*#n(luGCuEo!RBn;yjfW|rwu#4it5Xgo z?!Wd#dwRKyOq-6+iEQ7+4bt>Zlb@1=XcRQuK2b@*A;;TWffFO_*L_NBj!>LlC?QCbCWL|3U<*EsCRUrlA%Ml<^a z#6qPE9mS7mlcMfh?9H7W+f~l@)>i~%#OWI7T25PD5plhjhN_s&NIGz>c>GIPzhm#a zo{lri^BUd3CFCOswS5pXoo5W#*Ka*7er=RA*P#JL3BA8k@3p^D>o#T|L;K}pAVJth zaXl|BW&ljSu$(>~b;)IB^WAcghg}Pf=Q$S-%&zWm>_qI#S{4-19SU1Eqx03r1YMQ)N4Y3REkd`Fttqn^ z2KdcKS^0Un#fwyZ?)m!G(#jm;~lDwjL6w)bqt~Qv5OE!leYS(rbkYe^w;RagW;TK>gQuiHV z)y81YH=pUEDvw{Uxfwuvfmfe9A<5&wD+zXxuj3 zoluvL0YSxC!RN>oupt<1?&#%Fc9R@}=S*d?NTX`pA6)PO9bnjf{On8j%^FTmrzTwA z2qt(bY}_L@>X*-jUoNtgBu%1a*n*z-&73LAWZo7r+)dg^c6!8E21e`YMS)jDj*3@h zD~oFaL$(K#On>&eJqf7!;UC8Ks=M#q^_f823w~m~^0mT9U1_a!aX2}n=MvWdLqKTC zC$OXOmbbqATvt3r^oozWHU+e6ss0LQT1_b^5z&>7?H1dvRvyBX0!n#~BC0fg4R`Cv z&8O0yX3+_^wX1C#cB>*j)a*1eA2eO9j~WDj*Ot; zttlvOl5`h>cQ(hZ;f3DsfWHDQ)9#a8JwGR!co1&)JvVejM-DDAYjGhzdUfmQ$B!!e z`Xo(=$gKd56Y)=qy(?#Ha0ZWW=p8PRpPpNz1~UsfWLK;545=8D;iemhUt$$I`2ni# z292i6--J@|1xXkjY?#fA&FF!oD!qc;V!R@C`Bi`$%3Lj#2x$p|# zWvc43Ol14s>KGTtL3`Bx^-gw{TV62&6gdDgLt2myTwymRW{NLvZizN@IwNq|4U8u; zLtED(xw#XU*D$L>T1KuRYHW5KZ1DUzJIPvtraZrfTi}TGCpS1~pPNN`1 zaY~5IkQuE(jHVCn4nB#Mdw0 zKPsPXj1`+VPj^!0nxmFZ-V6GV7!BbiF8CGF$f|4-9H_XK}Mv}ia%zru0MzgFGH*SX(%82{jdiZf1 z$B9!5^KDFR(n@wi{jTL-Y}3*%0Cl%<>BQUN2~~H|)Q~;LqHTmPwI1-|$yYaQnfVZ~ zDCX27BZa16BQB@H=X1eF9`#?IviYL9ek2%}#x3>$@SB0OK?X^V<+29rOiOUG30-6s ztKX@v*l{dRJDu^!_OC|`rORe@ZgS2}p3ETn$M#l=%mWS+>i6=D-`|2J(otyM!R;N= zV4KIE7GlZzg9b6w=pfX&|!B2@?MYV z3($QKnVPf=H~IiS#1scYG=RM~n;0v0EB0KoP##hu7TKg)fY713k_tIT%`mi@q`6-e zGr%Qo1@HWeZBOn`;w5^%*<-kwLn~8plKLJxoU0&uIAas-3S=BEv%dRP_4A>randI& z3uOlib;mE$GVDCwORY*MA(*qAM8KJ}lXw7h%r z<5s41n(J`HbL(Fyfscj#*DNN!yvTg1eTkuTm$(3s4C(->?dRS^26%X`%&lse zd9z-%@UN@uKdE+s})X6<6nCg_^BEtdTDd zpJLLYQL|0d6i35B2DxGs$Tgx}Vc=CuAvx#KwY@Di!79&98nq{!lGl%Rp)hj=f+ypU9s zY_+v>u@1$UiLW5La&H0S&+OWj_4J%8;BJ7#E}J2Hh9!Y8J4wzJwxF7sxdV=XgR9b~ z4?VjTFwlcKxahEtsef&%;zB-^^?-s=?b%e7CW*m0U{nu!@4;pRbc{s%Go>K%#~WYVl$tL3i!-)wC! z&Ot1`>a~)|VO9YT5p=TbwT4kRl{D`?97DZRkq9EZr?9?+;LIxCox}ma%+2 z*Mq>6=%NXs)Qm&hjSJGef;701wET~k1DR3~av{^6ZEqfN2o+oScBm&opN^vVXKQOb zQ@sG$lC9dBhM^R5I{VAHK|A-9kZ4nC=4CL;=@x$9ogplzGFoA~<}%0WH>vFtmggkC zCM}mB+o2j)(gVJtpI41OBy}%|Vw}%lJ#qb1168@qDrm$Lvtx9})uMZ_k!xF9B`m&I zbR*&LWk9pvF))+%3CrCLPsJjJ)3%2j<9zbWPXLQ!JI$rP_bErfBQfm!%D8D4)?Pab zxx~j}>U`V703gj-(WHh{CZ1oto~IL%zY-CdrhK(Zv@_eUxRQ2nGf>*Jh3Z|Rg^+QJ zN-3#`^ti;6!E3bc)7_0HzFHBid$0Rm3G*p`yl>T!l8DK|nn?G!J}6a~_@H zbM)vKOIx!(j__ZL6~!h-3_iO}iWtne`Ya;$iYuWQosY7TrZ*Sjf&EUtSy22?j8DN@D* zYhvc%Bgc>tAD2xlvcf=un^E*ccy1ft-MK?4H|AUdc1mW?)Q*Acj9b&dyZZi_Pj}}w zGp_s6nXU@YLNCc}%8Z14=wxSNC>z;}xERZk!B`j^%NT>y6Uy=&&NeW!>B$Apo~<_k zcch`=YF@uIxKL74_)g}K`nv#?X03ecZr5N0f^jdSy;r0}S7~L5RVEiV?hf!7#eYkt z6rV}0FelcLs?JwBS^2yPL2Lov<-IL}5ufCQo+X2DETiD(ug9(oGVKkS$Tx9Wy{s5= z5=%0j(vdWJk;ajZ%+Ux7AP*P{T2kIQH=as|~m4EY912;fKiMc;Xil$bIb3y$2rxbJ zk)|~SDdN5?E3>{n%g?uWn!S4C-eC(<*Er5esHHvtoEtRb{!pwQ5MVpBNE7s8sTI`s zUhig(&$Hym9i1O+vC#uRY5>m4Yo2yGAI8y|^NmjjEt0cP{ljsILKZdmGAmj+uhZ2= zF0KDzi%QzRfYZ|X(o~g{NyxxOm$SM35tn`-5fc$H@kH!(P8XECAzKG%8~^x=y5bFA zWcU2OKE=%1p!m}R4U+mU_4#}H1j2UdlklSPO8bd{994Fw7g4_T-+vxF(W8~m!1UK| z3)ogD??2u~(L_^*P`UOq9t^5(&m5~KlxvbATH|X-wH+B4K3#^9l8rqZ?sAh^Ao~Jd zIOt=ofcosFf~A5IhM8Gubxi#$dDE6lzcO?$JB5i-q1`d<(yxD=6>aygI(x6Wi>TEF zDkBdJ!|@U&&q)ig+>_TC5)O$A!kl`?Il&auvsF)LWGCPR#(OCTxf450sq5?G=6n_! zQi%t%usUi#snH+kL?$5GiE@HnE+wzJmfz6Xl+&`LU@WEKOjl|3Z* zPIyg4_JzR=E}SK;o7?XDbj4RnbJO-V^?g)up(}MH=in4P`nad)N2 z9lqs;m^FN=!Livi{IwOd0c}z!{TJ^@YvBd5qX|(0;I?~W8UFK08_>@^w59E$A{CA3 zAz2|=`p=C^!)7h_yN=${H4;63@pqA`<)jMG+e5EpxX@r+#t(h>P0YA$PhtvVN^lP> zpJUGuxkQEdUuV&f7F(Wl+eL0_303psN2-ar{(%Xgu5-AjXjP;FE3D!5`N@XpFIo47 z1t&$wY4F+-WYNYOTPzX9w=%LF@ zy@yKyEr;hXgq%F=eC~o&Kjc-Mt;F-`3CQ{5A2%~aNCXN3Ls0cQbro`}F{-WX=*@#E zhvKV%WJz+nm9ue7WjWj$o|%3B!^>H}<9d5Y{N~%c;}LOw$E=YzFGsQ~Vk|0C5nF4> zviVS!y1P<8gwf)*Ug`G`7&bZ9ZTAVM&dVo2W-#ru)3l)Y3quMdcy)KD9~!?{0AjDj z<-T`u)15Ve1f8Cgv@CuykE2cxMs(QJ*g@$#7ON&n*W@?KYLhZ2yvtBZ#dn^TiUDm$ z>}d}~<>@)b%m3;|Xw_hX9r&-Dd(^L%X{_N9B5dBP(3Vmi1CcngdHk*li9g??1*xWj z!D;gVx5KewhHce~#~VZnA&jnaOi9_-hs4gyl#QP?x|J?;QjX-+D*B!Bxrj-6;Q#uU z7r^hK^kiO9UUr+$c=1e}mk6kPN(KpaZX2O^Z*|W#zoeOBIz8N;R{LK!kE+(czbHAv zP@R=wI9wlfVX1mTZ8fN71}pmOphhrYyUxp2mC{rn7vxGC z#C<1X6_0O%x$b)x&qx*5CXk!=ln_P9&SJA0#pOB5V3DJ_avb@Mu`}3!!ZmG2=pkzx zR8@PjH`+}JMSGV2Af82T$;xz9Uv6%aigb2@!e{dHV_XQa@zN#5$Ov}xC7AVlJu`LJ z7=(a-Y~?FRRTfEI!nN@?y!5Gol{N1l4au8%I)<5SSOmue-hRO-gT9Hnm?jMohqp>r z$FgxV@R;Qw+kLnRoo9G3@nL5z4loooDHBh+%~S>QcClDL$nK5N2$2|7nC$|pKtL~M zznGJY7lfs=0m8#|dpXpJLrGmHJBQKsl>IDfmPSVl^Dhx@@T@XXHR+4X?;1AD3l(lW zdD-Fl?=hmtS*s|hk9(x2KsDHxOf(Pddg*>HwhydE$qmK;`u5M<&pC82ztvUE=wdNx zy|dID$08hC^N&Y~nm)Lsp0uAEKa^Je0kyHUlLV@CnDFp`qyLbEqj>Mxcc$|TUjV`U zw$V?Q=|{J#e?u8rpN+K`=uM%I|A+;tv1AGA%+HloR?yb$B2XsjDMkXUc-Sp}x|cM* zZ4E(Q%i>?i@Y%l29>o5`PA*NPAdukK;|&QXz-=fru0qACJM|^6%*oGW=QiNcV-`B& zv`)``q+ZS?JmScvS?NuJ%%iML!A4Qlco!+3cV*Uh&ci54Bs*M5j9;A@FmA7~Ti?O$ zz$IdPu-&)}D44VU`2nuqF%ciZ5>OhPjEdZe(2~03A_L=KjB4y+WN>;;9=cqas4#vV%(hxn^P$~ZuV@SRm-p>l!D0o zCSO`y<+M%QJ)SrK^UuOZwB<%64hZxnS2bpzJw@%#Z?9TMU*rY6!L3HGs35vh8`bee zD02`3kKWBsEEI53sWb znt@~r9~h(FI{eO=eA|c+iar1?Zkqm)*(t<;#vm5Y(VT1(Ed0&kMVVi}{8vFuym)C~ zFh5soc-f%A2|~^zLlf9acwY-H@RlB~oG*_A0!$jnWVUs2B-a4p@YI8tOQ&gPWw{mjGk=#zNs@bIHx6~jQ z$Dn~r>LBxJKVYWgs&T08(D3f#Uw9}{a{&&TfyyoC6Z`Je7%aV9;qaiM?6!In)$ch` zqDpmAA93ZsXa}IB%o-+kl{tHWl`KcUAVWTDPLXg3eQ{*EoGE6`ZhQK>@_jCcyLz2U zH2yVMYI(1!q(%-EQC;E!61b&ZXd@OKXQW%$R}Soax(T-De3kHw^l-bisb@PCOgaF} zf`->^GPYjUf&Gv7@?*e+Wmu@hpc5yr;y$L&W5IJVRzuToIaw2zw5(sD)1&dpFwg66 zxvExa0rqoi%Z+xalG!iSH*yE(4yAq$--x2Euly14>t%}$12lQnfB);p<1_2cFgPL& zrElow|m(TSFI*pAWVLoBojW;Yn8lETKEYWo?$v_peqH!^GZ3?Ez9Q z3Yb_db@-yI>JgE+k4OzmmpneHh8=j zoYMCZghanZg+eyE@Ye86&yutHVr{~gf}kg?X{(Kjo+h6+y8pz%AOw0qu_1FKDB`SP-pEkNGt7|vi` zWBkI@6FZ>QvA%{Z>I+e8Z`dr)$PRz{v%zb-VP8ba#)g;{KdA0p*PA;Oau+}`0cY!? z{i8Ak<6U1_SCD&4`9Y%{T&Bm8Q7Wj=rs12%7S>}bPDx^ zC&gAEpFF<}$C5_+RpvP!Exuy3?KN>|ql_HSQ=qdaIncW(M58S>L3SDUwPY@NIp>Vh z=8)>@JBK#BM=q0Vba+VVOlNPWaAgX|#ZIG`Zf)QVNK>M-p~5SN6z2;a`!vzTS@cE* z&X4NRX=1LDJm{emt+nbzH_CSFur3*Q@6(n}aKBdSdh$tSj>lZ4aj%1g&*A*+cy=1~ zqBATp3aBXy3bto)kiKdex0#!XPe+?^>W@+!?)bU{9liU_Ym1_ZIZA)gd{~|1(*3!h z7-3XB%_Gl|NS|5xDu3PA3Ft-$ms+7BS{abrVTqI)Fp4d82GrAEymwPj^Z~|Ko}v9u z*7(Nj4KnY)kfxdA$^$2j^ifOe&nKP9q<)*3e5dChhrS+s*ONVEIJk-z(E033tqz1E z?sb-_!-m2UNMZcLU40LI2f)WDYvzBnqamsmfb#FgCg&zdeQk!T@wIr0&1no-oJu3% z#~%8Tp|L25zk1P+R@gEHw|CLDgVW2X>KA8YH8Ua)9^b>oPbr!GF7b3?Fd+KT48+|w zC?I~&QEChlYTeQIh%WqXvp~AEv`O(6MAdY698b!nL+f~w!F-uxmt*mwd+PIHan`hv zpb(ASK8(G)IzHBV!3c#21~1zXE*f@)gTsjz*QXxdyfys>vk)HMYxk1_trBJ;SIbXH zKz!A>nwZU$UO0J&gYU2?;QP?={T2CQlL~c|snKM~l1F5g;;+^-`IV%!vx1&dxcaxV zg=b^#SY2qPDN6b>2&pIIIuJT-&MFF*fI~&>_};czhbUC~mBpB}jEkK_#3* zIIVlcFQg8)e`?`+z;aseb?5GmilAf{$&&4>6Rmo3E&S3zl)rBvH-+^s-AynFnAl(+zAP8*Zl#$OU@v zgd&~<8`U75Ug)xZ+o+U{b5qJ#96pxzjr6j*RS#hx4$C{GW5noLsgUCNexH-fUuP2BLd+NZ}NKySMz#jN}2wQg@m!h)6%X>55!9WJ?BzO z7gkaKMoDb!4=H?(*ak32Jm25JcML#r7Apsba<=XR1y&Q|zFWr=*T|izL&RclHH>^O zJyJb!^FjR3(Cg5aOMyyGMDz+OPm0Yi*&Xkn!O^le6>#m2O{nblsqB#71uQzdSXuTa zZZA)^)7K)z1lc8b7xRswPl+GAytHDVfFp~%E|_G~u99@5_q>GU0|q4&Pt%nklL8*N-Wp{PaO89RWY7qK98rB#@@^pU4ib6jetH1voNv z-UlQ~Dh?*E?q)C*Ji$v;e)t<<5~R+uuzE4OUaY&&ahoW?lbiK%Uz*JI${FAD5C&^~ z0snDRcd{9ohsMvUF3Dz~Pg-3DYISoUJyYJGb51lBZ5kW}j3laN>@aXKf^Xc!MgPgg zTMTb|MR(W=@O0GnfpG^SMe^BlqPf{GN7{F1V6L0ZHoE{**!E99H5Qmf;wRX@s4b)| z4b;sB@}ICTYk&XdFGKJVm(?_{stN`Q;SHd)IlPDZRA4wQT`rR6Wt9>$?9Xpn)1LSxJdGeR00_k@f25qr?Q` zQN*4Z^OU9tyNz(czsNzQj@%X{xQSb*4)xcuB_yjHOPwosqEBe1Gp40@1d#U1m&ryv ze=x^kVqtOu;lh)s>)I~gQi?JiEw-m?qoa5C&vHIKH2PEr$^XRGlm!K7H|g|+YsP;VE0W>X>m7oGhK1q)_&tAdrk>ImT73 z;;r*20Y602$=~@B)ChMkgPezBb4UnsMCE)-f=I0*#Ca^2adp?aI3?JV@^fquZYO2y>8?D!BQBwN)>|CNwjZXmqe zQz8MtUHSKXUpDD<#462}Iw>+Rh`q2bN?3{d(m1QBuRyaEKSx>ZL!?2@*t6q+Cv`hW z3!A^OqQ4o7|F$_|#H+>X*`^B%eqt?)!u}{%ufF|j*_G_^6BF_1o9_93*eNZaTux0| zr{dTFz7CJJKV_@VgmL&?g1(W$$_LlSK5eO!ag23~x<)o1e7pdX#v&oE>;sSFotI_5 zjHhv3>R;fbi)s;~kXI9(_!t_f3mD$SOdn9g3k)j;Q4`we?oZyh2V8Kge)h{n9PICq zLWw7u2oQl2zUaD($xXMb|#Z3cBK{V-#%H9O1l zTd_-ewTuO)z3iNe%Gds2Wq&3*|LYgQS6DXE0V9ck8uR5rwqGvCV?TE2KN$Pk zf6xMVgb~(p$@Sn--U$?sew{#i*6w?5ZU4gPA-)U`ur^I@yZ-s1l2`+O*A1)u6kNFF zYlKV#vYeyQaGbEfhq> zEr#$!3gf?~7j0S9Y9)+qyZ(N}MCz>s90z63|8p+M+AF{^Up|BUd@77YBm5$D7;Dt( zuD;>`FPI!&E?c_Pol(`Rjdt5-ter@@<=+F-mdcclaTXs>iC|ml)Sf81=NLu~hTkWD zyuDhx{wkNhhXMcgF!}d)UDSX|zWBs!=`nUmyZ)CY{k^h=_o?}}TZVs@yhv&GKV-1m zHA!0njSqC+vkq9CmP8B5_U4Nyx#3K7hv_YBIJU>zm=NO(CqSh!zDaZQ2c zDM|cqk5gSV)81DGHnfd~{}G&@!*Sj=2m5gvZijcW-uFk_r>_pha=c}8_(Ut)I5nR3b0Z`v|(Wy$dlfbB+j#kfPFlH5(}EZ7ZpIj#v*Tft1sb=Eejuha4{ zaBMWne$t3BYno8*>t@?v{Afroh;4 zofc~#IR0wlZzPHqppSiHccG*a?!2XZ%9(LcZt&aW)bT;qdj##GlRt#~wR{uM`CG6V zMz)k?{`15CE!)F-vSHPG#o*TGG&ucST>;Q7&f?$bmLBwmca6c^&>(UO)uAS%9cQ`Rso|8Y+P8o~-u}@evIUGz3$K6c zEQ_B_4e`4Nj82g)K&W|lr9DyF9%9oPT4O|dp77pN8BJ*ALxj4z`lKuL^WKQz1|zYqp@yDsSVO~@zih$2bNy{( z5D2On*S+bW+&Jvc7zzs6cM|~t#dP^C1==``PstyI&#T?mC3IgOes#4evbW0%;{RTn zUH*f~#9D(nHleuqkdsEo7n#Fn*5D2yNAK~m^RQs%VqLyEWbS^}Q_>Jwzf=jXh3Y8| z?hlyuKtluBfP0Jks;OO_28vVHT&{}$eCd1P1A`%++EElDLc*Rc#ckzI4yOn`w!U1zDjnvk9Qxr6f^nw*ZMF z0%6KWxKNG%U#^sJ5<_0J#Qm1mN6nE!(s5xAK2UqyRqn5CSic|*>tuhYZqtq^%w&u? zShf14)qoTVMTRMz6M?MZ0t_-@AvRM+))dv9iF)TLO5wC%Mb}*#MjM8SS&W*>L3Vnh z#oj_*+($HAxEU=>Xtih3XARsm8@?Kfnn|Tn-!H7GxxnNt8J+34xS+;a5@{CGOT_k} z_IV}Z+B$>!goP^ZMa8$En&)t59ys|`Tx z4y^&^fBwqan#G;qxDS441$H;($jXSsVx zbR_bncTcYw@X^wJrP6VVfU{m2;OCh9#IA&X2Qq@Ax)Nq9f;80M1X^_y$(f=An>~>Z zMHzRkbn64JdSjSEE;!pP2G1pk9&r-MhU>}%U!LU<{!soQq>LY7Z^=vtQi2U7^X`(+ zF5{7KbjRWf-Xwcwh9lUFu)I(*RB>hON-d%*vlg)s%&MX;G6r&jN6d!LBRK;$mkJc# zYNBmn>4>)8%rhZ~eAyu2l>g=6v-gDYzph7&N(+hM{lJOu|ENqswv0gt3SkY@Rc#~# zfpA)ZoE?EfT3|A}ZJm}Kdh*M|+ zttCkKC7>p%_5GkK!ZJX}vK$%WPY_V%|1vP1!>p;Ry<`)80$$seLKe7O+<$e%|9j#6 zcW-lt3ygQJ?3KRzXDR9x#~b2)&3ocE?4=gMoHo0GaAc3v_?^;QQsIAq((LDFBjE2V zn|~E%Q{WPS&e8K%zlFY%Pk{p=W>@&*^H-*-@q9@`eFhgj1bwk&AI6!_aXkIJ7-l$CI28*zn95^N&T{*#XH28PXLJd z;j%_%#%;Hs_DeNwoVzo6-*0&r05W0H1z|Um*qNFH>0{ZM&ZFw&Q^CvJq(n*U7P^f3 zwuUd_t&X>i5oN|&5%fWj8`MdV4O_oZ|Tvm5o zv;fA7J}n$(r~3Xy`enOq(Hg`Odw^V3P61|K|9xZJ$6gvFRH^^AxdFFFDH->@FWS$f ztWc%e{!qQMLuj+Ku|X_}-DX<1%VSC>ih(puci9q+{8zM_wr%_n$I-wGG9O|q{Llrv zs%YCB>8M46t08u(6yM5L-a?LId>Lb7^~$W-HVhi^+<`8%Oaqq2x?2u)5riRg+BA^T z%?v9V_yV-{dq#|;A^3@leM9ieni@l)N(ov{HZayp*T5r7ip(eQes(z0lBh(6+u<<} zFSlY8mD7hfcAp{*Sw~Kgm1{&7tSeTkZ>E0BXE-I+7O5thnIJzq=wZQR!)6%R`#-m+ zU<(Aj#jeF|dB;Cvw%^xmOL-Kw)rj_ofmb~degBvMzAibA8$Oi_onwnbv^FqVg7Ss- z@7RhOKfNHmV<3!+=(uGv$D#G6cTBgh(M%ul|fxvR2Kn@0j`vn)>VTzMB97f#~{RYPM_XwPc#*7 z)7ao4)Xu$b0(eVobH zksIR5<@v2F_dKxm5*+E6H9^Ssn#@dQvCTvRw|po(O;c=+jwJ&;WuRM&qUp}MnU@1b zuUlNO%wc4Xg&L48b1fMN7wO5^7-Z`z3ogcsKm1An9Ys{`dPcM16N?Qnmj>I2UW>I~ zh>8>njG4bc{6E{_$S#X@6EQhJ&*II+;wgb06=VtNSYAx0F#AED1=xVTyB^E(UrUDt z@lEVbzz;3^k5alo{Jlu86QZ(eFI%d0IKBh_v9y_9hjVM?tWPEr>m~4E3b)(!F)Tx~ zDQfs@XW5+0&dQ~EsKRRnFKZ5v%lp{_m;@d4aU#T5?o$-~fU1D>S}IFT$I0u046@;| ztWI}>msedcUSIABGVMFHKmVY%%wai?jjyFIsF|Y0x%crat4hh6mYlF%{UM^%_;f@& zw`_6pjU?EqE z!_cj0Fa&PfMn17=w4^vH*0If_>PibmZ<82Ir+P0BpfF zV{dOyE;4}9oqw6YilRFsR>Hc#bLxUPJLmUOw;yLjcQeJ>D*pR1VZ;-|d)RU*$Fku+ zDyrmfr-|MAv`tYGLuvDT-auXXm0j6_mBLI-mw`|OZTdh?8%Lw#o`Kc?VQoLkR#POL z8gdKNH{aD5p(Z<3u?S=@N2BK`^&IIyxY@=lhWWNDZz&H6cTHlfT0yqGK>p^t&g!op z4fCpQC63>U>AByagqt5iW9+SdFnyytnUqI;dG&+cYy7t@w(az9ua+}*8FG+84|`x_ zEJgY`V^kYMYe_fJHu*sR6&}+|$mB9(xEKS77>+lR%+O`Z9SwN^?EQax)?@EXTCfUl z@!IqLJdj7W!+E1!;Yp-3ztOAUyUhyJ;Ubvsg#laW%S?+`y8=d$EV3Ghw9kt#{ zXm(S3tQiTM)@%fS$Mty4S4F0PeklqzdMn8Rw3E74|x+S~H$wiXUXcf-cpB4i2G8D;Z57YA!ce4cNU7J2sMlOJfJ25HqWhKp&yJ5LYI@7Kg zaf#0F$+rGi131UTXVirNk?>gGGB%e@&I1I~QbEcExXsNxw^b>+Ixd*w zKVy!;(YElE4D1Nk!iQ0QsJfOECB3`&%I-Te4{W^a&DUr{+T!-w!Fi!avK0Ok{E!pF zJd9B)f^*y`hT0shkXqc<;*QEK|*_z;e)|YIZB-5wZWF_|Ur>JodWL*&&`%)^o9^gt7f5FGqL8z?IrDkOX%` zB-&^fOvx=1K?FyK+>XtA1-bp;(S0&WqJb_vYZ+3Fyt~)z+M*`(Ft}w*o@+zO?RX|2 zTBcnpC7DWg>48XhuAVkRh*EMFg(SV@yQYx3so}6ht=IztnyU=?cJLAu@mx$1g1GOl zcI#lIn==X7#6PAqcbSivllu}ShS?e7Lc{(Gu=77P;xn>UGvV`XvM2@r|3=!^54)^z z)KwFn9<8Glj@fOuV2f%GjL$T}g~w4eBDwTLmNba$U-TsqWh7If^^*eG1K_sb`aZ$3 zw1W(LmL@@TyEbiS_%V-}yVn~$S<#g)0rKPrZ?D^N$JpS!$3(5-!o+^wkPZ5l=eAF$ zfg=iLzu}4Q8ocbmjTDv*yUq0jwB+>xfk;OQm zz*R_T^xq*sY~LjSu8}JQ;dlRIw}k#KF{@vsflnnG>f*Le`jOn?wd>E~x(ks~h<)F0 zWpL4^^UTHyoRRW7vnVlos;^w_S!87Ra^o!%@SCQNeG-zJ(WcZ6rljM=67F>E+bw6- zg&}V)^4jB8e*|uK`*PQh0ZyIE)RW9i^UJOzn&8(DcYEu+1lNel9D%gF7>>bRmPp;FqH;?-go*&|k0p}vcFY~=g_~LQ zBw}u4Dy^0ZRPiNRsY}B3+ai}gt2yWvdC4OLiO>Q{!%!4{V)*NB zVn$qU3;d`+>T7uFE&tm$_g)3E7~a9z`~HugxLm{ALR=K4ByaRNu4xzB%MMaI-Mta= zBp|cV^N_5R64(ZLs#70eU}q^?OB2a!Gn}B+N8D0*Cxc;}ffdS2+`_ z$W|A*#m~V6DtLLWU%R^bT6N|Y(Xj&b)wORn8NDv=F(>euvxvoPT$#_8FC;Q^Xoneg zU0&lENlDOge&gjoc_~=_X`ww6y1xv#U9%W+UE=*8K0jC)rHgpaEd+4e_5Sg%^?fCN zAX&PKPhU*+YQlQ_6Ypkc1w)CSpQlYa7&fmU{8#;`Fjrq5blR4tRD+?atfnxi$}aKOXSUjUHsnjFXkUFc)EGSwrgT;D`gB zC*r!*h1N;3z&0DmK#J3*Mbm@r7HWobSdg5pI?(-%x4rP zjKNXCc@iXiOO(ndC^Vy%UN}Y~iOreVD?>*nmzziunL}?=9tZJ8EFOLmu_@;kb+P)W z4Ogf5B)NPeXHE#r{vSMBP2ezdhWGDlxW#5l_ehy9+FPKzi>XfZD;}3QsjUh0{^Rrg zpAO6ud3=A=EaiN?{Rs2bEEYp%@;gNTSWKljjjTlyPHhWIos1EXYim)WFuQNp-z3is zbL2`5mq}faZ6q-E^d@4=g*uiU>#!1=hx_jK}z~m~9mv%V= z|Bb*^Tg|+QlnPJW_FGz$I8+wgG^?>igg0FHVMH*P8BS9q+oVlF+VGM?tNC6$gAY4@ ziAHs;g8WT?xW517|Hs^0Mn&1SZNmx(C{j|=rF1GGID|AJAd(`|NK1<3AksNBNDPug zC@COH*U&?kNH-%f^}Or*^L&5!!&)rXaURDW+rI7FW*bH59e(5* zO2{*IPR?E`1V|6{2qz_y-A(=PqQ_qsr{a5o{G%nPV* z`N`|z`%r|x*n)0a*}D#@H%?0sw*uNmP>|2Halt!qh1z49MfPWlVeowS7~X?Zv5)c+ zI5zh*=-O^GUXXzr%*Qji_cb^39|Gi`mbfH+>f4&vdjlh#1+x6(85ujHyLHN5ab$!g`bSkU*hCzR5OoI~sH4i>Y8%9OO-#5x>~j(A4Elqf4Z^!X^ln zW}W(oLjLkjN}mELF#u_;k=juy!ImMmejb6+1uvItf=oH3?%3VR#zj>}sl&z_@3`xT zmL4r8gnatP@amUw-LG9dw2hEQYJ%H^<&XG+LE>vc8=EO)78Yc&30kMcAqQ(C*zenk zmbWJ>ER7fpJ(qtP;F2QJ*eHISaM$lefVvnbSeh}*F88Ps&yg8gtyx|2wn{E2=Ht1gD!g}{I{;WhF7B@t3$9E4A8v~jJ(ea6Obf=uNR^FdOf^37MIx>L2rd(Z zejofSTtB392aFq8@@m3?QR1 zD~R2>XY4GFX3^KQ=6bY2J1&LYoWrn8?-Z_$842J7wqim!2d@o=($T(=X4Yr$CcXB=Dp7j%ROG5Ns!c+gE zY?}WRSbnGF$M>-!ovww`>e;}@_5ZjkJ<9l>9L7Yh(|R7hbW&YdxSQ*2j~tN|^0ktJ z*10eCR63N1sop}uWo>Ty>C(ABSIJ?(QfN~oX7(pH^4!0{kzu;%)&+??zS4i8XJ-3` z9B{<_O+3+v&rbu0Cvk8DP(;*xxMThuk2h}8C=7~WkcaF9(KDh#A)=gBL zkEE%;VdFz~BZWGS39bfIr;dkn&HR%18uf@(mS>q_aq{Z^;GXI&3DyAK9Koa&kVwyM zvs-!2f>jX+o(7qGc>*;f4eA(S>VG;0V;t}e9Po9(xy0W8PK{sggYAilKWdW-3akY7 zQY1|{l!TE+ZqVDDg_$W(;WK{e{I!bs(n^GNUSU*{lVjYrHS7)cP7 z$MJf)l7MnB^D(SuT95WMIT)+N{xZiERd7w&kbj~{B%PMGOjs`lreH+IvzK=oZ9HDU7i6A~255=(T~re+QchQH zRe@M0G`}L_jrbuk!LLFWVVKoDR46tlyUWNUBuL7wx%g==y+~T)ASd)9mOvW^v#wY_ z`Xgliz7&6F=9_*Q41Nv`r+E*Qzc&st9U<-hd@|Sky2$k0qU3!^GWZu-7(m2cXk_n( zi4;^*YaBKf%01Z)%MIPD5&BtSc`12qD@(63H=YJ)SjQ&L)nY4g;58P@q zBjRkpXt8^`E(V(`@)EM#eyK!aYSq-5=^mm%U`#};49P%MkgQqw6Me%hl{>znyc8C* z0$G2++Q9}rbr%MgZb3y$tuh#CG2_)^N*Qk#=UsfXs7~<*yQ%Jnh>7iDw!>7w``3&K z1a%oE1a-IkkjdSQ@9bDpF}bp8KCSO5-PZu>L%%GIidmPA=Q5}eZ$>=s?dU{V>jNha zwZJA@KF9vGz{c#PC=DE?@r3a3$V-|;Xe!DeXNy`=9y zDhpb(D52{JRq>w4e<0t>*|CdpO2PLy>j!p5*-N$FER_7?<>>Li|LkYc=LIv2`J>E# z@$;?3+Zt=c;TNnCK90L`g5Him4pbcSKXW%Q~E=p{`X z?ypazP^{T~Pb49^m;s)T{?IE(pifi`aIjXh3)Nf?el$*^TRA(!)J0I}@XHRDt4b@1 zuodf5@~sdfR3MpuaGObzs2c|!tMeP|xd3kSL_%W*jIfW|q_*y*R6RT~YaB#9iwzMI}aBw`OMf9?n$b9eCXn{*FI`=Jkt=YWGM6o51VT zrr21W;YpkV*yft^!O{q0BN%khA;iK;>2=!rCPy*pBxxZ5NMce~!hV{nE+wfmKmfRD ztx5f?l^>uIg{6uXDFsR7+*=I31;AwqQe)ka7|cG~?c&#OdSXnO!d6V6{@pUDE6bic zo)Zz#rc1r_9sL!K7<1MPj)xv2fUTC1vCxex zaSPzeytu{R%JB{v0w@c_EDV7ppNm8i@{qBsMj=0BQu2N-==dM1^`kXXN9=BONX^|p za;~wP{Cm_zjsA`#qh!8i`uNjPDWb3bzSqt=p0Qj>J$8Dzj6Ysgu4K=v8(}HNA+XP_ zrbMBZ@et%N@q0{9sOlpH05hLxWzANUE?e|ZXYfui0-?2S&BOKf_>99Dd==*2&{3nA z+?Tq2I{b)Cwfb)5Z&6*d<|q&dV}V`+j750>V^IO#e=`={`E4v3^HC>hF^tuBISfMV zj89#YkH;VbtBgP94g&N|N=B6c-#`hsFk*h+(Gmd8kx8P_1A26SA1oEQmVZ2qM!I02 z#b5rcyME=5#A}?${MZ9ar6Jka&fCfu=-v#m50;tt$@}rM&My6EV{I|?LdRA}@k2?U zq}8tBq#qm&bSnT>hU`oT2)!6N!F`(x=SzfH0pFfS{XNyQE(E@bx&czz$W9oxQ{Ohaa zZHnDZ_!8iO#QhQd{%aNLLUWNMhNkn!%>KxoPlKi10{fGb1LJgu^PA4<7!H;*z2)GB zpp*T{jbB!e0~c1041m?+oo;B@z07j?kqd-Tza&(&5-HR)HQ z2UesFTmfYaRA^xq6ogyLFcM^+9?BH?fJ-ghvbgQ49It+rJ`Xx36Hm^sCxp&a4pz** zq;VaM%~iV`(Mmer8Xe)?8dHbN;uvYpr)p91|H%RI_{jmOG%5P5Z&bkVV)I5z3Q&Cp zoE}91r^o6D#EqS6nauSv94!Cb=o?4LHWKodTwiYn=hJaf%jUIB5IC0Mj`n9kF%`8wEo5a;JKoB z$&B^!Y+0PWxh9cNrYqv> zx(xvNL$Rv#7x|<6H~9nPOFjm{AJog4*S;AjgQeoUyy_gCn{6%o{$bv|az&!6$ze}N z2wE844|8OBk2l%&DfLFQnzsg%vP8KKnw5Cwv{1(KdyW*|j0p4lh`J-AA7`VW(+3#7 zEr)x<+_AciGa9iB{uu&W7P`n9usV$r_@7yW=x0$GN!<<)Ces^#1|yY|xU@$q-7`{p zxkOBSqSS~VdUNu^U2BcdxZ%QG3wpWNqNM)v1S>n+L9_yjvnu6j{4>G+-Hycl8&nd` z%MsD$6ct=`RUdy`{;dV@Z%$iFWidrjIUf*ISBfDHo7wlc0H-ZT$T9)%h11r?mAO>^ z9u|+%7F;YIv>;;FEEO?uv3%uSMz$mtW0th?Cb$Fg(KXEPkyy;m;(nX;7c3>GcS*;E zY^L3ONpso9Wto1t6|{P9+Wz@gq=j6>%hu}kKN}U}QKrYvf&EE>1YSqT8ruw`Q{0XT zFUw+yqbUXOs@!&pnacU)$lGBQ6LK|#;`r3H&K=DDq+$@$Ms5+11a?@`w@xGR zr7WojXX}=YxXlWJLM&7zH+$r}O7sdZ2p}+x2kVPkrXPX`!nB>ZKpAFumu31ivE8DP zSJ>|GrfE1`A-Usy$xqR#Ow#~u9Q+79H&Sqm9`IJ_B+qb=ZflG3?#NMugV@nb*;P~O zXbM?$^a^@Mv$%uuRb`0xm@1gIgd_z-jphMT=xUtcAKBo);=({1<_TO1)R5U`#2=++ z@^ezFnd0ZQGOK*Q(kS;>)AW6x%h3lX?<%AaX?5;Ob=a}i2nGp=H+8N*!mO!c2Ep1) z<%REsT9)i!vK!uFVq9K*C);Y#mrYlAKkUi>#R2h92RI;m_&+%yeQ@hjW%*GlnGqR) z0}`ffzn!kLPniGnYUR^CkCF$G7k}1USeXJt?b+2MUnczCt zLV~){2iNODI`WcJY~|GG(4>k0lOKvXG9KfR^Z=W8A@Xe#mm%IOs-AG%MCF&pp?d#> zT{J#oF703m?}`78u-NNl&%hWIq#76k;AoBR{GVa8-<%YHyDSd%W>=5;8)LUxD%6Pw zwl%mc`wV}%ZJc@nZW{-O4MlWIR51BR?#|NXx^|t>MN)5K#N^IEj4mU7hC9x_ZSCql z*TUd>lfDn^#|t5Tq|`MJcdd&xz=g7vS-RlPto%*6_w7uVFW&?8wAl(Sk^-)G1hRO_ zMuJ6HYO~g5J)y+R*se-q1F}7#SDT9X6d3ACJ17CS4Vxx4{se;#01!p*g31-yJj}x# zZUXMKLF6G~x4aZ=+I4n5;hNJ$4q6XQ`{1gl32~NJ1bJiq4_z2o^5VYini!4syfiN0 zHmwr<8)|Okr@DwG$MQJsZNa!eF}`-ym>eNld$2ySu`&QEFA$SI*QD~6{~+G@+ui)aI}XcM4m=UV}8 zia$HMNLo}gfv(vFGb96^04j`w9=qK4-Z#ePXMwruSD-Y*QQVB##Ux**33=WQWdB30 z^}8KcZEGgvQC(8~O!Nn!CFFy{MXTo)nov)b!TGcei7wiDv*A1LU@4U$aa?=(LW-RA znGiHT_!7MrAVp66hZH%Y#9Jc`Rmj&#p(qxjTa1fgUW#F?iJnuR@hkpC4Pp3A4GDYN zrn_XCxLecvXa~pP`XE6+3-t(oRt#u-DW0Fm*u8X&!|_TXD*xqDoB=pOC<7p2+l1M% zEa_b6kms5JI^;I7A5n_ho)2QV@mSAN$cR~@P&RUGEK;vl&;#7Qn=^O1?!TP$N~L8} zOQD;1DqAmOH9EVV|3|R=H!MN`ONtRFKhfyY65AJ|rNb10-)R5F!Df@fr7f_#H%a}R zF1l4=SSc|ATEC1ODT=|N&w*ko*ijFdbaxm^NJa?x^%J~_(jayT5j*S3Oils*n+IV{ z`4|+d)G1yU!ay7;--V#7L(UaE+hCRiWup{Cb|PZA6*o)njW3~v;8oLFG^M{pr=R9f zW?!iw0Ys;Jeu_>P02Gln>0I!oh=#^iLvM3303{XF1}GxyCCS>=vI=T6kC@y)hQ^3h zawab8E7prlJ2r0R7@4RRXHxs^;|1iS?9$FCm!@%8_G z8tIoo2K4TjwAC@*YFfSZprns~z&zV33QYI?EM!{!fbzrAat7+-7P+0mYUT^ANl_Wz z%kL6XrnCVbRRz`P2PPjd@lyVz!|$U~!xGFaVC_#pI)Ct{F?DOBBwE}}GV z)jjK=+^>rAhN*fwEN}-%|6|?p;>HDtaqF>aV-N6TW^aGS`Qy-f*e=GANXl+{stgCN zZYZVVkBz&7#Esc??(PQ*l{4xMSOF1rKQ&{4x{su)CIhv&<7yFO-rBQ!R}of!dFcTg zqrym*A(r`gY@8yQohR)8sz@fTC%wmjzTpG*;Xc@Z?rm?n*iQ%GorA&Ik{u!6{1LU3)g!pAs4hxh^g z1V%t0lT1$>&K&?Wuk`>^fSjh{jgY5y<79&A&TE->z zVkKvDRFj$nVc}J;rBMz>*X<4f^#}=lBpFMVq3ApUkVQuE_aR{}S%pMX8DkgJRDb%j5w ziD#tGsRZy)ci`8$Mp&L0ilmfxLyQkHu*%}OFXor&9FUk2M>F-x`FH_Cu} zH6@lZE>jmGgkk&Uu6#DxIFCNRkrPg-sazZj6?h35y#(%IMlz4Qr^@_-BhD(B5)Rju zyKX2V&ahn0y$`^Fy}Q8D#op?624J<_=v=369IBN3;y#Y}YyCSP%G%d6T~_)smoU4* z%e^Uq)-yg-_2}ab0l>n18!%oBe60wvM{P`2A#@)2?s*=~1-B%Zk=cB#u}xCT)GdjO z?WHpECVg4=qfh^b#>+CagG?|Hee8F@DY2zP!=bkyRzUk<*auw-Ot$jw00W61+v5xB z!uzqlqsNU!X7H#vz|<%h(Qt&a{Mgp{J8tz{$D*|_HPo|N%Z*#>h+%EnhGnEuS2yQ< zyQ30lRC2x{lmYT^{(J&GA!|4?4G+@Rc`6n`C&m@QaJRTr^fxf^z8HUX2K8$HqyQ;} zUX07&(vCwc;3l%2hZ(&8Bk!hM$4VgN`P%-r9d7y1dt4XR@Mijwg3`&4_51HV{3SdV zBT^Ja&~rt>?&DTbko-?qXmbD$flNdvrp1%7V#uF?l+E@Pk?3nu||K~36>t)jkndnhzouF;Q17QmmLsr@2|#q zL%rui9Z%?A2UGH<1Oq~SBA+4})YVc&crgpLyDd4LjANX|k-u*XzF$VUU!d=~dit@w z)pV_VvL(Z*$K!-}#_x?!0)bgzVX1)S-&oaUR$xf@FKUjJFq@>EfDI!QP`B9v*eTG< z2Xkc)a1k7D;dsyN_N>kg%cytVx4ZnJ2sPTe%;cQ9IK3{IImAA=(au&me0%Hkjkr<_ z^PN!|RqIb1iDM;dw>2J_A949Ikfo#UWcib;?{Me5!aR%)iZv7})5FP`=X#+nt(^3& zjA^6^i}|wV7vsf6vN?7~6sCim;MC)GM<8 zbuj&tE+R?pWMzE3|=-D~o%16rJYw8{;8UD(OF$$2<%{d385nE=?++IXh zRm1_6DuqMQLTq@jq)(M{9I@mP)0ErE^PxHgwb=>tA=Nm{$gBtiMpZXdC#}L4GSlA2i7}-&|%7LN9Ro2nlDn9MlwO1ZJcL(|IbD4L=w&VpOQz zvl0CJrSsuqvQ@v4rJMfQi%(%M^A!S>-o36nFIS zLBv;3iv*A%x!AjxIqaC)w9>9w%hS1bvHB_~iS5MEi!ZNp0EP2Np0D7t?9=9vj=&UOKH^|Oxn%?wkJgc zE7hdoGkcrqdrOY_17)Gy!13<9)zJe_Y@hfXs>YR>!Gps2zPNr7t)9jD*mBu-T#BcB zyLk?{kG$U&@1)25{qZkh0lJ8qdao)+p4=9fw|LRjrS3p2_0r(;Y_rO>>Y1$y%hl13 z&u%i7q}A>XayOrwx*&D;?X(J~o&1O%O)KTq33xZm?Ygu_^3?8jxh$z2jyFKQ>Gq)ylNaDS0>^ z?HJZ%dg&_DCl21|D{EIwAq+AI0|Ls9lx`xi01yI}HW-6Vi`F2xGttQ?;eBkX}Q9w4>(*=%r) z%X=NLpeeq}s#vFO_FY0pUl3Uj-&~s{kD8Qln3ZZ)Br7WJv*se?;hEiTAtDOrFO$>n zUP^k-Fb%eJop|MB)8qEd!ASKGP{s)cBnq=rcruXHThk^rz_cpso5NRL5PJA-jc3&o z5Jq>_U*!2&RQYuBP5lBl!8`aoBb6>kLmB7sj>#bFC--(gxL95TyrIGy&UtiqP7tSu z->DeF2oe86O(P>4KL8xTvDz%*{?>A<6Zr)rJoK)yhcmVc1s{NRG&iUHk(DE)f8fU( zFt=Xi!3_4Uaqkh9jzPE3cP)*eF^VEFs;y~t9_iC>c|mp95rgto<0tHZx`Ldf`;ti% zq0i-r4(1);yB)=Z1A0~vM!|SRAKW9^u<&1cRx1~JRw$0&dRCK*{tVCSl*3D2zgbnf z-Zp@x4d_{2?r^!#v$`zD|5MMZ?GHUGh@CK?XXOFtS?ytMYa=_@F7&L7e(725N_$9@ z_x_jz_UejJ-G(l9v(H*#Dgcden*Ab3t1|cr>loce)^_t1^f@iG2NCe6o#liM%46o< z__W;V=1Xk_%#9t~k7D50;_`+Y2M=F=3wkyBTy*4MH5(G(wdwIb|E}f1wC!QPPi{i& zD&SOmRfV_CbsljJN1HTzF)rp^IUj3aV5Br~Y?3BJytxc(Ri{_wb*;Dy6h2|qE4Yah z=&$3IznbR89wx`V0mXoWAK%!ARQSH!i(t54uOnKlHGBMixi>YqKob_EsV4bx(%Pp| z{Mm1I(R(8No-SO>l?z* zHI|2^&DXedR1-2~8Lu;fg*bT{rtX-`oF1sSZPlNsf%?w6%F*Yjq=jY~0$%bX)?wL} zpg?-JDZvQ|)OO3<^huD~$`CN9aoiJByp8^=5^Hi5xxrJXl~g+T@Vo5B=*OY-NzJ@R zSAjqH(tWNi$>PrU&zs`5?dQt96!j@6ZFh9S7;7?1tS9+U=nqHFSyWFVlnz7viT4L$VBGAhOnhV0Z89oP3-Iw33G{ZBz_2Zr~JA8W) zQ`$~b6d1UmEoJw`hkggmF@NN`$8QhPf3a5VXOu;0Xg=3(yAY`v5xV1b7z)o&!o-)r z0+I~DygS2{IuQguly2Jf+e{f`$d#fXhaZ=(Ia)?SW^n zq=eUpoBF*oTxYY#f*@hc1bG#p!D;fNa_l-m`_`Dp0y}oo9N@Ng&8%IGBdIDSB9HEj z)_SB+`^)HDH-CMy^^A=lNbo17>nDcgXJ0kVV)uDK!I~G*RdEy#S5F|nT%%7ki~xs>>0s33 zlX?lmC1E##;0$g_65l<)B&gU~#~vQDcdMdEe@>QOt9!)$cSC#|U z1C8-Wcy0mh=669lTKZi z~6#Q@NW$R!YJWku<5N%c9nr4Jld9GGy)a)!!w*v%h`)?G>za-G(9( zyL!soMbD-nchDds`gp8zEJ)X=$SO??y?B;sVVCya$-ME6CQPNGb)LT2B$zRAk-v>5 zc*j(mtKHJ5>OeqE6-KP&irUzFDii+zu^i< zxE{%K?z1)&6bThd?t=(<{i$6XP>shcws@g-vR#o|s9Iy3d(U`5bn=rgHng?4$Wsg&-% zfN7Bt^OP+DY?8|$5$3^p*J-}h@n8Zw?z5_$v)Ja58JUzDa@=u1ekjB6lR>pbv)ea& z+R663WM{eBZwrU+H5(tJ#Mie=SF^(Nf|39q24>_Z&6(1!!lFEdd=RCsZ{6{FkxBo) zXij^)0p5)LL~cvfj#rsagm#nygn_;R@sbdrx$wcNdVUov%a3s=4Xv6S?P?Wbi*8tj zfKK-s#=Z~4A? zV+@Sk&(Mx7(Wxpe?k|6JpsF~2@9d4|{4Z-@X$3qo(yeY>}H`Z zy3C)w0oc(&wJa2ApoJBaq<|f=E=4;idVa{`VjQL#aoCCAvKe?Q#Kzk1h@=0>ib0fG z4Zk-0DC*Wy5g-k!qM! zlj%Yyg|5L<`@5_T#vVI?RADbDwNn5ve24fXFgNKbTR|5+q+yA&fsodrTzPQjp1G3y zCYp5az5f}xIJ}o43ckfCYJ5gz+r4&pf_$2G$K{9c*rV5B?d9FBgkGDKx|Qnoq~XG- zPIJ;oyy_XZXEzztA9iv&7|#tg|KNfg!r2^*J5zZ>PCY0ggaeyADMADMBm%b)w+ms)2e(sOoN;v2j}Hl0yiO#d45T+gM~okn$O zp!b-;n(}ldbcQ_(vJlEg_1snYXuXK@#_j7w5nC~u)^gtJL!X*C`NBds0V@ZUNR@4| zydISTK0=<2k6(4hJyY!qeWZ55OlogQHHN;w{x1Ce@WFIrvu~0Fm%7C9=2vTA^a+=u^qI!|$K0HCrrqT3NQA2|#hPU+=8JGJ?Wcg8(;e zi_MqQf4wG093s#*ai&UAv30_jfsbk=EOh7Vl`(ddi$B|xruykO*N)-QMj6&FsMn9D zLhoh`Hea)Xw`c(keD~dtufvWOmnId!2(cfZ?p~`&E>6E6B*p0ZKF@E45O2m&Q;=#% zROGa{qEFmfzxcAE8^bS@XLd!=_yvvLJq=X1yJt~I92-?WC{pE4WaLDx%p=iK{0Wlt zs9n{O9EK?w0$p#C^$K;V;^6A%e@qRHabB?FeEx1c-v9M=xG)3)5pf+a3LcLi27|nI zI^NmN7`mh=jYEQm1+Yr)Z41>IXJ1gjfyEPh3}^k`(Gr@1wOSMGTxfKuPN^)p0#|9j zg;f*W2IQNyc>b^(2^E@PZ`6?oZ=rCKcF>2D_ie-9E4uvO>Ro63sXZ9@>DqO_<_;Nh zS#UW*(XO9BZ*8$VpX&CrC0)r0E_FWLS_5{8Y#LQ9{(}w*vO2?*`cPm2zcf&28I=YP zsxgmf)#7VU+}WX!A;lR&`d{kBwf$Jx)#!FnA1;T9aku;YeONX~ZUOUzlo8sxqi9UmAUu%k4r^dpi1T z4(M9A<`)Th?7)xE>qXl$=VzjZohThU%4E@(j^3mtp=gnb!?PcGYw=WU#8P{YKX0Ak zh2ImvI}>|3<@zHeD-j_*X%unBCl=geBe`P(;~p^?6~PJ~2Q zgoqiv?+H4V!|P9eq|ykEBWrfrQw#0ImwL&BKxtlm9(P}TS2GhT#dy|Lwg8Q7#;8gh z(bi!T-Z{Vb@x+R9dx2Ey%c`e4V07z#qu&a9di~?`gNBpmDJTgnTyKcubMjW90*sWy80yi<{QzH;g>jay5xks`+YX zvut4R^Zjm9xAGg*2?;?bvo~P(OkMzrkdI*H#@M4thR({B8v$o@4Gmx0w;Wazl8lIC z=V86DHgyJ>aGu)7;}%KjVd_Kvg1xqXHQ7f?OwmRJTE(PKlrU};ku46MhiXG9#k7SV z{cfMX69Md11oqA{+qRN1kETq+d&4snh4P?22Q_WN(kI9|MZGew!}t?qMPI=|#f`Zt zpQ`H{4y7i8yB5wElBRc->CbxF=sY|8*2HDN4P0aIdG14g;Vj#`tOD7%lxOs(OGy;I zQXNH0>(hgAdNf}>0=nTEzhoOZGMyb$gQ9`6k&eU2!gOpf-I&Y^`7@8v#Y160n2-DXWlP+>Tfk8Vk?K zqkD5twU^d=!O;NAt&418vE&s~@C5^pRopv)zg!+Xe@)5GL#`a=Ho@6o$iS*TqPSm# zB1v)wwh&$+{hjaV_O|u`11HpN--;vSU_!<0zo`vFcK(gvlg)I%L4`Lt@|6hIG(;6Q z+mMmk(8J!mK)Dj#BUEZP?Nu?{_{K~%Blh_xpRbM`nRvNzCilb^juy`K7dvV7tPI6o z&q~?U1_ed+Wk*Tg5%zSn=(R{XuLx;bV}qqRKe1v^m#046{5lt_QlccD^5_DNUDWh! z1*sCyRU@u42;DaDoJf!dCErfc4kK0Ox6pJB5=vJs zc%Mng^WjJ9neSEaV?xi_vy+z6UNBU03&pIS9tX>JOp zi2jnQ@%l9cxH&h`|l_da0FRbzGLelP*}&We85AD-yPO~fo_#FR8J z6xT1$$OEEL>brB|Io#(Ss+oPFC-os2;;>HGl(sS+Jnn@nJuo4HUaOvVvm{;kG^D1c zDkLP9?v1h@^5J(*A*78m7|ciVxj&v%B4Fxp5X{v2s^KDH5HU;T~mUD8S2eDu+Y99%7Z@KuZf z^Mgpsv(pbYkzX+{oz|_f2e%T;=0`lJjGR5JJrTbW*!!!@kDp-^oM)WR`md>PYoUco zw;di>=?ImOj1ls@fBXPIFK_&n?EYesy$FzAy2^83@hkCsi&Gf#<8EmZV!D>$U_nei z(8u$bB-xff!^Z=;Bg@CA(DnfS_d<#>-jWl!F1nLlqUwlS%BhT9BpY>i-k)C_)k)<0 z|BH1L6opI$?wFD1ut=kNh`y8^U}@d?a{T<%$lZALoI+CI zxi~-dXg&zN&?`D~oxcI)DO|^Pzg5#=v>3FXCsYj-AYvAc$}mdy7ad7X7ZT+~AL~7D z{Kit2*ITRYE0k78Y+G6(QK`a+hV4ODk=(aIBBdWHmu*bo>242A*dN_w?2|g5A0%%B z!OsCh2jZ3V*AE7l1SKOPHCGbT`kvr~Jet0~e`E#;yubBDR2bj>{Hy)Y`8c~el#an; zZ)YkOsL*SRxJzok^b6ebmg3ZFR|>&;Sw+v+)7_wQd{J{-usLAA{{6FzN$g_ zG1Gb9g6D9Gm7n`_o84vJW;mm|8oTzWc{PAtgl*{9mUFVd2GB^Z{|=2DK^p(ycDIBTsD26Uq5(?cHuZgZIXxYx);VkhaWw6u2cd zjSukfYjNE71_i?|UH$6pR7Pst#cb5bvBExE29%S=d{QGS;1qdtvWZq(`x*n=e$Np0 zI%ER2m{TY}?ee0f)(3#=%-$^NYh}E)N~XuvxHC3E<_DF~xqg8t>fI56e6k*5dv2!r zB^I#|N&;B3ySOj(Ru>fj0Df+YEN>h((UFgy+r-Px`9b$D?O(IuY?2PfHGr8>voE7p zc_IfhvIhP#QxO0zm*4L4<1esldF!i;^^`jKj=r-D?qu|R(mJq4XVC53K67Sh;1KVC zx(<46{G|`(Scn}bY6v=30zFuoE+o?$Zfqt!_B}tDu*e3uc^9bN=gpb1gi|5crz`pa z9#f>O3ITu!RpZ$iy%N6^hslkyV>j~!QPL7d?c{wSUdv#l_WO%oP zBU9L+M@hq&Ska=yV;eA1P%)_L$7Jpc0LVBAZ+)ODkO4lnKKeG($FpeTIkM$^di*1$ z_$rtEYL9wxH{c;G{a2EDugv_jsygjxNJ953slMj;H6} z2*|@x7a3!TMsN6*%%y{6U|VZ~boM3>7Hb6| z_%oh7n@Bh&Wqjw!=PU^cH1NnzfL`(b-`!QSu2Ou1>qnupN}2fVWzG1ba=S^b{;Ycg zi*E_|u4RA^YUDDD33&`m$^kFTpo5*R_vjS|>XbHhWnp07UdUmTM|gn45eP^ZV4VB$ zyA=x^H-p#d!Yy0J2dZCcndeutiPXoA9OVZus@mSymD-*`&rg0Nn*W}@-0;OXDF(}% zYO1f{J``TDD@WdWE(;J_28Bg==e6ZOe0=Ji7*0sA+g>RvEL`e%Wk75tElIw}G*#4r zQhoIv3(u#18RZUG%8c(;oxsbf&)dtX4uNBe^%@oic*Q{3gx9uVC;oaf?T(@IK%2?l z_)g^uLq2&i^{~V>LeQ%TF3NP7enZTfQGoWw;BMZYz$NMTtqi zE@KPld`W$?X!VoZ! zeajEu`(SrZ0WK_U!?#JRuJg?D`zzR22CGBx*MJ1wr?;QK?%ZO?KoRxVYnZisV5~sML#{p0dPnRznbsH!?O?48AMg;EH~+hb zU{?!-clOfKhCb{aIEOH-v4S0t!>hx!X${HB;>5mBqcuJT*O3g1q?6KMAZB>Nhx|0d zPwk0f1vV^O$ucfTW{2_+sXIy{g;ZkdfEr>TcFn5FW1kpafhM&|rXArKZ>)_jWg-Qy%_k1r867WoPKs7)+A%vH(RPKyK0zTPCS!P;>bsZdBO5j4)YB;`_ z)6L7zUQ5YW^*52xHtPsmYY{C0i%{TlW+~g@?!H|BTtMHcicn7ilo9|eko4*hU2r4r zMhR`zxO2Fx*8Ol=NpJVh`=j^6UM;5{0`A6YtQ?5XlQk+)+)4DCtnrx^N1An zgwPB&%2dGLTs|F&L4Dy0QavX-bM`)#+ulV;Pz3?lbL9=IDKT_>+>@3TT8CCmp)(Kr z#ua{F31)ixbDLU)HKTI|tzJd46z8h%FH`#^pr&_yi*#;`-5Z7Z1sc;Q8Gw-G-F*dD zVM0g4pDznDi+lpz#jI^NhfIowJI?^c51r2*X&H5a&oz%H=a&IG(Gh*li%H82iRLhF zH+5VfMQL8lHDnmfq^G>OUTbUjk`@Wk9!@en4{hU=xtrB)XS_xwI;0btYTYlf6YyTq z(ou%w)ytX@ozAnsl?-Ch>osCKkpqn~?IDNlvn%iZ{kCw1VKA0Jk!h;`TV3Cf0> zDmA!?uSN}7_r9A1J1^gjf9^A)ZL*kO9s7J5KTec#qf7CWFDawgtyfz&cucYG9Y$T^ zmGp6Xan;Ps3%zKXLFhu((pR4sq|oW_i6V|7eGGCz-r@P?Szz|y4fL$ zU|{uQmfd$gX(9VwTkgT&83B^HjallJSoUZ>rp6E)u~;I?=~Rbp9z~9=0V3X|GXPef z1jfS0x?cp4b31HG2SEcD*lE#n8eQD|F+b(T4KL)zX)ol)vHzAE_xO);k0f z0yuZxQ9>?3MPBJAi`(mi&=!HFeuUP>%ppiZ`qSTJpRrwxnm?vl-1<;e7fbXra5XkaPq`tp|Pw8siCq_1=>x9-8Ee6A`#>mB!zBQIdWZpbk6a5wV==}9#lexck*#+2Hftm2Z zTxkojN@Sf!_QUCM=aY}sfxx_3ll3}K3AnaF(xyXI<0>_F-Fat+9bjy6C!1dzCig?I zano+9Z=_1q{zd8`_%T^^Rguv}%ci)~H)+WFj=I#T>*uA!Z?a(CS^Mf~8Ys<~o&9aS zzqoX&mo6q5nZsX%%04~KQ3b#V1$z!aYvS4iASRob^!>2}r|{klhpU8S9w+hY&Ky}K z@}qh<@d*<772w-}0Ree{XPJp}p9WOQyAQB8y!OWhZu}u3&j?7!bMv!0FG1|)V`9|* zubpAL+5gR*;o(J6;}+A+niVzQJ@jl#=v{4xBAs2h*@XI|Dx1yd1pwyBe}|J%X4W2X zf2)=|B4fK=hJ4{N9!Ape*)VS}?POzC_R3X}?J2*V)^J|Ug;vp>5cJsu>8?Dg<6i(C zOL6s6hq_m?5cf|KWBV73Obd0Y32XRFpzxSJH`=k6gz!46p{Fo$>QU|0e4x1SaFq0@ z1!ngx7h`Pt`)&_Y#a9`9_F^0n62yyn7HA+MW7gEm)?nqFi-o>tcOyI(ew2IM0s){6 zob~j6?ph#~L9$38$~N!?Tac-BPl<2V)1|p!hHJzUdsatAou?~k*eBWwKZ}izBE+=& z>j_;K6ta0vxx>Ow-#vch1OgzUWbe9m0jn2 zPbznMc`{rvsO|uD+tSw%#F}%jwH;547!+VAf{IClz5(<}79aB<^B?Sx8?kySy<$e} zX0{lishN>6X6k$#TiTB$CpwLGVz?>m3xjWVg!0b(TIXfKZM%v?kvq<1AcA#8f;fCm zWA~a{ZccIy9TQHL>0K)62omx6B8GphcLRF_V>$P_{M9_?nGi1TW%&R(j0=^2=vUQz zMmnaY;3WP#J9NIKasUUZiI7Ly+-4o$s=`n6ai91uQUJZ6{zC5#OpMNJO0aXY+r6-j z6P9Lp)RXULahAy2@+R(t-7CdNt;C`QQz}b*?E-iyx3|JpIa~X7ex06XG*q|nQxq(r z7HEd%!nlk1))DXIDGfP3#^Ev+%j)w%$p@?Kzr81nnbN5h@k};y-nnhEm+bUJoA&j% z$w?^?ci5kriIgrXqj1w8k{E$BmrW_>S5NDKI}AJ*Sv|8z;U`FHVTYc(h^Za()HoTy zGi;%5X-MY$43^WIi@}MTEY4)Rv zPk<+>z$$U;mv+u-L5E)x+=lBos2SQI$!+PI_ucFUyiq6>{=kjmhD;r%z5BP>5$VA2 z*u1;b_0()=7vQ&$v8kt!_klL1^v#CzhPOB{eaF>9h>$UkP4Yy;7w^`1T@nniO-}t;OZP^m%orV@YSzsrC*Muzd?QdiJHAC&&9+`6+kpR@BU|4L!{IZ zt2_B<`zoujO{s55aGeJS<3Je|0$g`+Jg@-BVz>&u7k~%hF~oj6I9-CM0e2FpJ0&k+ znB4bzZd+=BCj{X{^fd{L)INBNib-VRNG&{Z^F1*Vs&r@02JfuGe0Ip?62(fBXO=8V zX3=3o^Y3h(ur$IGKT@iI%~vmLFx_PdzBy2^q3l5}d-moUAhhprjy{8vu>oy3vK>N8#rJtTwwWCm1fxRPn}58XdcJD> z#ntc*oqPWSP`U`xizujckkAQ;f<7vUjb0-JOaKAt5J03!RTSw( z9z~^tH0doMgeC$Z^o|rMp#=iGD?Vj&&e>=0?|RSo5 z#75MQ`6LEM798?3UV|j7yVxL~?@rL{=NErL)aapKQ@k-f=z7>qqB$JUETwrYsNpd#k5t&s z*5cH&-uN?c8+C6EnDU!>dG4}4?p_{sd{eM~F}p4p2XqLU8LH?czg!xeG2h`+1ivzO^@Comia*p0403U^|Uz2l0@t-*jak*;2NX3XTWH zzMHQWE8uAiB#QV8dAQpjWS8u7JrM`qt^1fi>4eEa>fs)~x`&`40JIW|r6dke+iTNb zo4Z0p^UGzb2>r?>66GL-GluXvTNo6$%D3N z##B9LtzE|Nek_AmrqxB1o8dZYS~L1SU*p50k^7-Qmh*Z4+f9nl3y-@s$OM>g)U(uK zX-|19J>(vw68p=gIPT-{8EM%nj9eU`r$+NQ?pvl zekXh`ZyyG$-pO&v7WWP@<91A*6*BXYrswj3Wdz!dRv9X6evj?#gZNx_AFr4T0?e_h zLU(F50kO}rt9t7uw=U9lgv9DN z2zqluS{tY8Qv(<)Q?{>@9xa-re272l(l1u|jvxC;N7K2Ia>mav5~5d})umil@yQGjSul z2BS0YTje5?QR^*GK4$jFNWE@ZQU+fGGI;z|4*xFc79R7~8wtlw+wvZGO`5+{a6G>_ z2x~5AN%a;cXge_gydO1a^EQx?2-|(A6nuCvP}?Y)Uz6%XZ2ps6?Mb^-d$^)m<}mWj zHEgU&c<@2tg*RGpwJkQ^IQ0Om^>^I}$XcYuzqif{^w~@4ku?h5{PRAcV*cPrN%s!FLIg?vSzlVKj39QMLo?*kIZ_6% z_fIo;Tp5Bpn4vm})j+suE_|n;EKiti3p*GK@8YxKPgRUwD$wZ6{7cWHkL)SWx7N`b zV&6^%u}F_cYd!nTnWC!)!`dpptXw{(W!%%=pP#>5r-N^OkbPQFvEa37>RnGTA*c2d z(B8{YS7Oj-*zz0BpE_yqQdYYq{KBSXmXYMmt>;4CePR}GZ=7jv_(RTv1IT%VG6_j@ zZhkvs%wYSYP+xT&$ay%{Ks=T>*o2C%noVl_Q$3IOgX97GTSLkKC0xJyG0pG5lY`Vv zZ7g3?vu=CUonu)1r1{#LJk#*l8&VPjr!rteJyXil!Q2gie`{mZEw~n2{z!)~Jrzxv z7V0}U(qjoKz3WNBCK|=7XTqOJ*6f@SE2yLRnlH^|;C1NMyK%vUtzow_rM^UIMBhYW zsk%$e`FOI2v(;g2xaWo5zo_0}g}%E9S&gj&+Q%RPU!MHnd0-CP?EZLnZuM%DN5HYu zH{HMi%6Rc%g;DwJpVR9ep=YK{aSGB>hi*qV}Zs6LZiw*ftBB zi+*F`y*f!J85|8ITmrmsTLag=`+%gJ$5HbfnCkMrf)w1|`_?fH-3n4{zxm#`nO!Roxhn_*idU-?l=2pqzevcF)5qoK)sC zDeit~?ER{-dZ|ohcRq8iwxz7$`^VmfZ#P`SL@wCJ7pOyvxnE%u|7`hoUa{zqd%&Q) zT;)>I+%c~NZhBi21*zTaA1bWCXBQYCy_S25wVlog)DD<UV1ThFeu6|y-%un8A!oaJ8kw>cS>vF|UH~FyS8?)^@rl=HkJ}q8woqztkP3O+&jgD^PrmZ>b=lAeJAm0xc!*r~={I=F`f0 zsjP}_o{S!$MC@-_9IdZdId#pQo)_au)*x3)5hBkiOT~rpPPb;X|G9P#s{QO%1i7~g^1Yx*W(zBS~LLHCD z%eE;3IpDLOBbEBGGqL%#2TF3AOGTo!qObk(-$j~QwA+czF{(fbfZg^~BP;60GgRgF zjSlm#8Nrg5@=Z?XD{DGAnf)f9IrWi5qWc*oq=U)Qh+1r!{8CAoLsCDZ%>C2t2$$de-em~(cj z&mS)K#`;DBPVKGQ_7c!)R+z! zG(@E4)%e*Gh-=A&;QjDh}Qc^upOUzQ#wWPn{{ieNt_t=5}D#{b(STqN2x@ zax3CGt4Q!%s<(v7`uxqyfRSBIJ{{38-EKrG;8t)BwB6j;m1#CPncXmOgdsN$}&8+G(x2F$wi$zdR96E zo@yxm5%J1qF zd9U>zjDgAC3}ZO^j&w|$9j$@b*Z?7ESD{^dTq?>U7{A1>gXi;)PT7XaOJLC zH*uSO_L*ILM?^nVq~trXav0=MoVfdtQ)BKc)e{rie4^Z4m;dO-+Io?;_mAq>#EW3u z=vJ%2pX};CvYXq_)%SMX6D>7YChs=0PG;U>*&6*p=l^-ED|z2@cBxr&fj8!+_tw1U zbo+T;A3nO&eH0%{4OSByE5EHlZJCLpAxLR=@tl!x;hVWVqW5a@j7SKru~mDjUPZS? z#Iyjvmq$m>4;CBDYs$*x=-%Df=Q)1QJ_Vf&^f(118%1_@ zHWl49!zMHGV^;!Vp1nPO;@j`%edfGu^5o&8Hyd72$xIy{>~|*b4et}vPTR=LrQ%JT zX8LYmZ{8#Cpc}HUyk(euz$P212;Coi-8g!wNM);R4JQ97-`dM$`#GOOuR+0hurofN zT_U=BDvF#@7yEOKm$B;192hXVIM9(I|Ec`av9NRburEK}C-JZQhg0#yE#HQ=gSc;2 zaagn0-dDS0y?rsG=j@#9m(6}-*A9M=;3fT3j6(Apb0w7Kb07;QsDp$P#|~!8X4=xs zO&vuhjlVW(_80v0T68<^Sh-p1eBU?a_8#tSNY=}T$0;-fXsH6Au2iMI#QqV)e!1Mw zt=-~tW2`F`bE8mKgQ7zLW~!&^%p6w)fWGEjW(;*)7(VBY9W(78{|SEOv!kL0tb>An z=`uzx1DuOT=MO%c*D$2yt$c0KMJ`nqThADEGRDA? zD&TDfgpTBc8Sg*~jOdfcD-8RaLSo339_nl`Bdjj3_BUfz1MZi?X_yS@+|=$l3(7;Q z2tz73p}S^-qcqfh_}&ijbh>r zGw*iMAAdP61EaGG&pB4q=N=sBg%@f>8>QrC^kw2Tt<;uHV9}A!Ot>HB_!hJZ!k@%I z^7@ka!Jh*;Fq`TCYc~MCU3>PAcls|2QAf*ijk29K!00&UTV2sfm1nPh?U6s1;P}(J zaRxDfZfWqkGsblcDqZO!ZyJUq7CguT3RcM-WnR59Uw#9ZiD=XkChb+y1`}wH?LbSkHV2bQ%vwA@t z_j=~~?@>oqD2GT(50rR}MXL*=Difb8;>%Y22M!;Pc6zG8H|!~C5lhLULkN{ZAYKJ0 zFv|uSv=s=|AYch;KK3a5eWy1AUtV#y@HBCgw5#y*+7b_Udfmqtqq|vY6u+bIJMh_9 zspA-OsB03#@3)shgeH&IsI=0I3n$o9ISDhliI*f%a!_w|J{A2iTj+qlFc6(E%err9 zeAil8rc5rQB{d|tTGk8kt)F-0(I)!r0exN=*O6U*#=;_M#)t63!>DkXLq-h6f=){!myr z>4~gEd}J%4aTsSQWJzb3DeWnByOy79N-`eXu3tKp@PfNnsUR@N&qD4yBd4OBRKWNM zt1RM?Y#d^{=jSc%MJdVRFKuqFR++qo@6$hA%8e4=iZ2;h3JPa;eag0WjzLV6qOXab z;#cVWk4y3E9C(~19S@CYqL*G79uzF_#a(>V2?=4K*AFDY(ldxGbR~L_I&1qES;5QF0G_D|wup&No zL}Iw+oeD_KW{$kGg=+Yu%-pL(3u=%@bbQ9onmgEbjFVMlgJMG_-cJt^SFxr1RPqeh z$V50^FRR0}gzKj$4e1cpO#vdORf^@#J0hm*f5}zhfLzu6etsG#5))nXI$;bK5i$&1 z&BlnYm-{Lo%)o!DA-bx7tJ&rMZUI0buy_W7r1dlPTq+wtbv@2^`L{o#18E)Vs5C4J z0;~^>eiD^XP=hUB*DaH@1Qb~?d~G$k#e4LZT2=8cwW?yDe&*8LG*6sIP0=+@=^?NM z2IVdI9_~sRhgVaz7Mv5&Rau>NoSD_u5*yej^h76$d3}%a zWo)w4R1@f@n2H#EJ4ogHkgTqohEpk6J>%}!u3%t%hopMSTU0%t%;{3B`s^7d85{n4 z*2?D7Q3nQSL1C1dz%*7#zqB_AC{-2jJ#^r<9?mALIpq)66?ysw-L4Kem(NTQfBVR; z|Ifjb<2Z$#1?TbKL_=EJu_FD4x6VN)>F+egvkdG5}Mvij++an&@F7 zT0mIF_Z4ee#_;ZI=}M@_=Idpj_^;PhK6FS5Qzq)6&h>bF7 zl2odY)Mlzmp}+|^kve)ygO&9%T*u{tK3r!qiH6(5hd_4H+}GNBukeA{1Prw?>*%@8 z%*0#4x!P))#INkJF)e~hs+CaYm9c%qJ8fPZ)v>@J2ABAa@Im`xnWJ4J^(IPLw8lCGaT()vK9X?I{R4Vrc!J}(i(Peu$Wt6J(r($E-kA`C6 z#f3pjNB*Z?OE08DNBGl#TiHaI1}~<`AETS^zxF*tUQ)&-fuDDJ)FA1Xm)Y_S_2^os zj|p=7EE^Y?+f80CueEkxw~uhsUB^Y*vCm5@%Ioa=f4tL~7^&Vy)^w(K$>Me|+G~h7j{y^# z2fd5q)N#EolsB&an|1j8*E;-sVWW9;9V~y{C;F^nAExq@6#h_fvT4P;?J?Sp+IHbh zVd3V=z-&y$>iYhz^RxBrqeQIn>0(rC*ht_;O; zJ&MoRRn27S*j?lGULT5##`j~X2Iw5*>49Ljf&NLeoRUaw6PK7Y4tvx?94vd^==Ip= z(K_b`|1P0WXO4eDiWlEhzah^b36`St0i2uEadcfNE*7^Q`+|(hgHiWx=-gg%^N?(v zq6(ySJmH+j9$kTx2g(sS3*oC$>~^Yg%zH=k^YNtJnvyk9HimSeG~nhq3x098P14X}^m1j`s9w8w8A z2ET~aZP0y;Beq5a*&{M}Whq~}Lv$STWK*+Gt;6t>_x3(9c$g4Gj(p|Wm{auX`4 z#U5Ro6E6zUc{7Ve#rHdnpv$J*tbR+z4QQ1GW2pD>n2eB!`kdik4rAX&chGv#;+`D4 zoQ;N%oc(~ir_L}1*L+=`24i3 z-xO$TRqueD(9qsOQ5NVWh5f6bNJ|mR7;vfJ<`?ZmhGTzUkQx?2f(1PhoHI{dy42Ez zHaTl^dI=$j5G4EZM^>4+(6@!@g@GS3@0x)TfnYDjCe zTgU9N(1dv0FL$twHb(M9x$o$}mKGZS{6f*8!Nj|!l_JDQxY-itZR6IP~E3W9+4I3oE7Yx{&BZ1DnSmiz9l!G zCGI>W&7EIV?c(QvC=96!W}V`A<1^pdg+y6nGZwdH-S4|TL=h*x-pIwYMNAkWG4&CQ zGElX)7z9G>;q5~&b*1O|_Am9y8KloqYm%M+RBN%}aAGvFL35YxOJRJ^521pcy%=NF z4&ImpiYq3FhSZQ}xiy%6{lQLl-&$Y+W_{3~wzE&N0IQI++3)BwQl4UIC$<*oX)#Qj zXGbNJfGUd1;Nr=QztX-V5&a*DkT5UE3KE_~U*bMN@%ICDCp0ZqsoBj0Kc|C4K%ldq zGqrb!(gH?<@aS4nd=sMI#pU;Km04{)$L`MB@Yv2m0_SW#QX35?kLGI_BW}?f)Jpdl zv#Oyl=hc?>|CUBBv$-QA`-YJHAvQxEib6lgr}uJJZ&DN3`lQo}xJmixLAJQ#3I5LU=RcRIz>mOuvg)ZDCq@e`a=! z%{IEif_96q`VRMW{@jI?WagE$yX|-q6Ag9CeR!#{5 zzA@cD>VYmW!;_r7Jb&o`EFw_@^EgWsEIt|=(YJ{8i8l03DSX`X78Fm3kjP8_dJ{aY zYm5e~z=y)bN*}rUSlI$1ZIA{M3htc;B?re`hvmwdIu`^Jp(@dHVtk5-u4U8SD-E71 z4;yK#oW^~{U#L=H&?Hl@i2PAA3Or|Ki7XVdMVJ02qB&7j9UZ7`gPO_|$y37L*VAqs zOyEz0pp3&yN{i}aGSh}J^Qx!4Z!;%ZzPD{HZ=?A=yq@`#4aACm|Fa_;jC1EG`6l?@ z#wCvV;1aW5bLaPcgqQX-0Yfi`n;UAEpjQ?W1d)Ps4@TFq1rN|SZ@Nh5>uH+_-qWBx z64gmC(|#}B;(F@~Y3^Rs3B2Xd$t`A8njOor zmxbL)V`|5&8Z>7Y(;}f3a&7r%%~OGSm%2ffzmx27?kkL45uGJWFB6T(7K#v~e`wpaspmmu+N0-FG9dg;Vd#DECBHk`&l}dw-ORuRf@I<4n4*hOXGm@G4fpukL15N zZI7BpcLy7ch(2>mE16rm8t(A1olVm-sQY=noL=DH5#T>=i9gC`TaXN`cXKqKIl5&m z$O6FK!1y71aj`s$v2ze)u^ydM_QdhwYI}>dWMI{$Q0D$#SEIrwJAc6p`qay}Fwh{j zxP!^jWPM1~U%UyYf-5ZDR#)!d73wVopH^x5yEgr!0R0wv%RoNopV1$4z53}F>)pRU z?ABA>QsfAk6r&$48BfOt)Me_oh>@RkYiw@Ts>5VRTF}pltYQhFYlYZhledEY}KN9%< zDvZW0)yM0dyHt~vBT@k4Kd_TfYV0SwC9w7%AcuHnT6(a>Oen)|ommPTB})=hpbde~ zMx++|*-yOiG{{RsJYj~D2BPe1$(?;78o8`SW(Tsy9#o=!nRlUTBCQV|^)HjmyE-Vl zVpf;C!o;0+_hZ5soN|vHI_VjA8&K-;Tv=->#-bD<%-oMc zf)}BWiuIywHxyl`j_Z4W^Fp-I>sS9aNQTt$^Y6ea4%Hmo_?6!USTA9lWshtSRtD1k z*vfY`H>XMIPR<8O!@RvtK$$LCbu~4_>YiRc7>aSUt7kW#l5rW0P6H`T2qq|HZojQN zmcL?CFjhp3(8HRaTP;yZ++k(;qPFH69q>Z3wKW*T_rj|nb%tF9Q?*5H7p)iWIanV5 zmr)ep?`l*(tG83v#{-EAPnH0DT?jkfb>v*#yhN2q9p_mB5)y^QvWf*>pX$ES2CEoC=!GucnO**gjDFn(F=P?bB>S;mL+g9<3z z{N1ajW=Uo-i-C;J%YhKaVt$Uo`e5plKCcqSu=A~1HIexYUe`#*Uc!=1IX9K968Ke} zectyMHuHg8YT~W&#mdR^Wd%73NLIxoU;V$P6z5nB_{#E`<-jPl75DWc2TfR%A`tNO zfvo64c`;*Wf5o?G7q`w4#qctIf6i)On$ch0Ui)e})yJEKIVL&Z7~}|g+W3Ah=C32> z0BbLsX^>{M0Bf&0gI$Zcx&h+)bXkAfmQ_!szql7Mp_yD!W?f@-7V*p33j!D6SUi1# zQ__&*Doa;Ga%30vvAD>J>l&8gw|CBKQ-5HV9^kUR(iMARN_hi7^jputtn=&-+lB8RtzX|4 zyKXmocK_pDF&K4cDP@$b(cx5dM>8DW;%3}~VEKHTkJ zy2}U)MR<9r>@!uT!j+ZU4{yyFmpA0qoMD!Zrb{%6g`2rc*if>mb5^%`j_Ji;1n{NdW$S=FE8}kLlj89tUT?8bQToTOFJ}vb1qW#?s zxJxsIpNcURDad!>I*z|h>hJG($gh0O?eX+&*ptg)+DY^$KQCW$uu9ZnL#;isnxLD$nyh%8T&L7{q4lYKh?$ zz%@+(_ckG^c3EFQ_#p~;*0o7Qg796an9pMywmyKuEJN}tx=5Hv5RCcc34 zME7qZtuQ^QUXzNx7<8mNXfgZ`pfW$e;ltFHl7eyB^&nZd&S}Ns5QPwUM|UUQ>j~s{ zi+qM+6xNHJ#JzAp# zR$$0e()@0#c?lmA65=sCQkqSAS$y7$dLiP!OL`&XsEefwdtzPx|5hJ5)2E30>Ff)Bbhp?Mgs-qjtqPcj1Q z$HWu`^jTFqTWNOgz7-Kx6xe7@hd`Vc=U>4HR^x>`rE2IxgZ@?euvt3!_q#u>?U)EnrFacQ+87*UTQ@4CF8Hq)dIpR#I zTP{8SVoNY}Tt{ymh1kDKLtc_z{`$=`e>ToZ>r?meYWfP>W%uvC&cR&?m2<=`Zh^U_ zloI%AZX{~{tj7TMo1Z!?tnlKH9!RDyQ#&&H8Vs?fN6I<}OOHn~=yv(987c4gC4?VP z?Jtvjzt9|=tuvAJMl!X9IlVl%$VZUY56Qa`_T}jY;l-k5c&qKgf#*g*Jt~Yjp=U0Ch5iP~P4uPU( zKOysh!r7Sf_US%-+7V~E^?wc2fqV86c=LDULdc_6sv-q z#gO4Hv^p>7W4~(=i{s2n>5(>a&dn>?o&ZK)x4%wNHl_}6WHg$kxMsNXH?3;iD-HjF z<5YCo5Q;+0YU`qW3X<+HphA2;dEw;Ell{wi>R6Up$IQ z{qu5-9e}r=ZJ#kw{JjWu(^>$#ilbbRm>bZbq?=pZppt5LVO%j(h40f3yk1VctCFT2 z0`;v z00u-Jm=f-K_vhNfFP$%Dw}vGimoa-pcS?ayq}0o1L*S^pj`!B76~`56mt@whcxpUOq8S zkg)q(vkpJ_WQ|7B`j9b`Fg5RC)LwQJUK>SF$aj_!UgZw(`I{G9VAm#*0?c6?Iw zpgwI%DmA+$hUZ93|0luUAOgHMJB&r*=zH@9)Kka#E~+`^Oy$|sCKHC->lXbK9OqQh zWOqx+pUJs(zFw9~#cKLT75juHXoy*3S;k5VF}gL7TEeIDb_B?NVT_Xu`jiJTo*P1^ zWr#nNS2wi*2Fdei>Y_Z8thB%~^#e-tj^m|6J{~u zCUEL1BlINIZklJd$q&K$Q{fUOPi|Oi{=J?5`L-A6J_G0u?2)TSo{JwQb?JBv+!g@3 zf5Mj#D2z#gNnz`X{6;28q=xdoy@jO;HQ9YK`mV_d z#pvy-G}JL#PWU#niW@VWkRymRhT=c3`B=Y@u_`4PW)?fvskClbma>B$p5+alU=`;N zx$RgCmL+?+)`EdA#&f)5tgRzD!XR%CpUE$0xbcB^k0-P-*hf9*7@D*CYVq;uRx4v*0<7`7Co;S!Z6r|O16!hsvROH53q}khP53M$rG8{!L`y6kAKdlKfpt( zeQ6g8wb24@et&yyu~N!F0q%V_WVEUkV~u(e;|;U_Wwli%Nv?W;erMCaq1~OOF6MF{ zGy*k6N~CeBUlH=}4bxkuhZQy{-~XnFfKXhZWX8*8(x}Y3_0Cfz-P&7?6skji5Uw`a zVStC^Y{D2UnG6G&8op4aaPkkV@BG#@ZkY)y=+7I|3jlHKP32N}QE&9id(F`&kHpfQ~D3Cm-p{?ubcf#r$jmiX3HIX`aJ6jeIR@@SZy&yv=@T-0r^AFHG0w$V4PDs0d_F--m_EM^CNK`#8_>>2c_qAyQALr)e4a#;K zK;i&u7yqu4LlvL~8W-PauEiZa_)VmPuT?QVT+H;rIQf4gnc=^C?~Urqp&${6o!esD zrB*;Vb`cF-B)Tx&?~8Z~XOW{r#sxuH?*4|R*52^LrhwI;RvJe>Z>o7ZDL-p(6&a)e zv9&oTjS!|ykH^9-P#ess)x5&MGGK7S4bp_maMb>H?nc~(MzjNFdbWipUFl?_ftnYre}$wF zh1e`za%G!-Z0pcKdRl2~WDByYZL4#?BDa~3f?Fp7X0o0k1T7}KwhYM|>l(h__kyq% zA|hELBPtt=D3qp=Rca;foY&0D1;%Oni{wAf*OXq@nt(LD;-Du_|8VfMr}Bb_{0XKX zlJjUQt*q#}$rVk9%}eg;yL8M>xyQZvPL}S<7@J!w8*l6%OrMP-82e3b*D}(oPS?b5 z%0m&$5!zyaF|Nfa7vq-;Y9e5)WA|?u;iMe#pNBk-s(>JUTJaBo)a;?fuEBE~TVz;l zJ=dtLcjwGRUoo1WQ{`E-3L^2I%C>xsmrXE1bm+yJA<08SzE+j8-|^-~|Fh`An8p0u zl5cSTo2m!k`a8I|@DOzP(+c}UHAlN(vchMufY4}Pk4$7J4cb*NC_WxIoHM9$mW(o+ zoY!k|oN1-8v2+<&Xkf9KA`+-wqiCkAPfEOQJ3?;^rf66OLf|(i_cZFXj2G+7cyj7} z$LW|X;Wu3b)dm|K!fcFN#nod#6sE@nUQ?5&QscT&JzvhPzpm4cpKn0 zbe|kq9*MR7X z7@knW5$)A{w69W`@jVP+@5D`pjjcOiJLcQ9sF*pu`jHQqUufogY# z{Y^%dZd$qy%aL&UMLDS3>hzly{xeXOn$B7G$UI(BODMF%pgO8!8xWy6=}wD(`VTz4 zBLR>04>aI~0H~2(q+e1*0%DtDD$MQZki-iCK=5K{KQ`AnSc@ZN0vi1PkEjjPROhq* z(gOJZ3(cX2v=BY%a)tI_Sfnt`{L@jm#FiNx59`_m7fnEI?3Sz}s1-Ms%&mChiE10vyPOX;tBKN{#Hs;Sse@4eiRg(~r@ISIHF zmD*U95eiu=F9f<_Nfx}Io4W*jbYA>R#s>?jZf2p7BTbFS9Ottt{j1?k>O8QVAvK_~ zvk4up!+JFERr=0##U%H~d|l!flY70Vp2GvbK`y4jAtg=?BYRUOg89607|@1X9V#aqeMwmssmoc}6h zXjq&j)zD|p-(pMxks@j?-jpifZ_EY!NJ10UOR8(4ycfokNAw1x=2S+}Or0Y`)a+e^ z!un_zZ}{%1SM6oHpMGu%eHb{c94e;>X#VJPwbc~Z%Q+pHya;cm&#+vmjJ@a(AzM?& zw<(?7!xh5}nHQWph{t_`cNFmXXy2mdErF#4I-T!q7&oP3RN@E2eXf|30+#Y7CP*p_ z&n^Y}F^VEOg#aLF=pwLeJ%hVfIyGV}>dy2t#ABgEp2m6dq&YYDJ*1At=q$**V#)#m zn(j>5PNqQk@25tXskEMh;!*4Q_ogzCWHtE(4{98#6ttd`8VZea1=zEf&wEiKi^f&> zQxjyp02?YuJDBlXmz+|ltWTmvdg&OMq8i$g0gSsW_Bv*p*76fx=)s|5Xc<4b#Vw64 zimllI__dv_xu!UUeN<~xaC!gJ!}1T}wmAXk8Q*$L8X{|XxI-Dr6LfCZe&!aT4(zZT zfVjm6aw@7zX<}dA^?${kD>s<(~jBnvkg)r|yK zx|ax_iG+^Dz$X1~T}npY$QafT&H~@O^(2`P94U=+HN4%%+|rnVLnIb*XYI;RDb zF5!&t#<)m0`jO48VTD|@w8T1h!^pv`*Dg~_(iq3=3Ps;9)kwmo-PDUTIVBp@E~}qH z&GX7~`{L&^OM+7|{C7H+_&UZGHetet@n4%@jNJ-%=&!8N_luV!e;={PezR>UtBHP; zq#Tn0pW3m@)Maz^wD;1;V^V4@VZte3P!>(p++5dI^=weEOrf-0NDVku_A#tU302I+ zGRvXXsEyXO1fv#q@9)%cL4f`AKf5rcfI}t;TZ>X&97pcgD3S2BlwyxP^7x)1_i=I2 zOCAFB#MP$D19Ao5|HRj|Y#Rg%HXj(jGIbv*A-Nw}?{XntUAUVSx3^MTI}9`^tq-a^ zXZFVS%qn)@4CHO>;pXzq4ExRf0`ofYUC>$+m*rvW{;Fk&@#~RoY-TZGWCf)Uq{xcf zKWkTPi<+77?O`L8D;@n7jc3-=ObIb+tY0lk`uI#!_mbgnc-^oWIkolF*0|4h)_wQ& z#cL1uSK5INq*30Ng-`2#r~NXAy(OXMH_eP-n|9W~NLn(ST|#VZ|A0wkMXR{@k0`wp zt3Dp&U6qb3?tqM#;#b8AuTx=jr5=!||jB zyDgOlnotF(8Qp^L^K(9Cshl!8Lr(e~aP*l*H;jJ40(-4RglfqLrF_3Eg44ti0=%cCOIXI?7Fp z$>+Fnrw0}LZZ)+z$s1_-syr3Xxs=LD2?Dx#rZ{b{L56O`uU7vJ{AZ~#9Nz*xCON2c4+G5~3zu`RQyZw4Qc`J22 z+gy_8{xHX1_$YHgNHNLI8()oB_w+R&8&92|GlqaDbVtLeGsup z#*drQZz?kjf{a`|*!4BMOZjjgdl@Gao5sYUXppbh@_v);GJJ;ZGFw>pGj$8x=Z`Zn znxl6hgAZfPs)xTch;tLqz}sG3upYs+7T_NF4Aac(%a56;?PM@X_Drumor7kn%v`8^ zq6H;Ma2_yqclA!J znN6%l^HN=s>rmWVay~eA`W9AIGJ7>vM=2wxguOcKozF_;n0Qv2(`*qYlkU$3Xu&t3 z$Iknsr8fJcTjPODaX{wN_1>*=lATS0ny=UP{^4}2&QGrUrNdS>iW|9(%gcMcX}k9b z^-O-l#vy-oT%@?D`0}XZpu`ELnbIML<(e#~p?R;xTywt7^QdFx$loa2i`F0>G zz2)OPz(loe5j%7xs8LPNFk=T(8oX{z=hD1GXvodI)}H+zgufht7oOG8O8!O z4q6{=R8s^KqKJ5N9(QYxEcAlHe<9=4$;7fM+u+Olv&L#S>(NFDoV#>6BJ<*_3lUct z0f-E@_U<^}A05Smi6M*t!8csen2|4b$FfEAS!oWy*k5py<|d8@)Nz~n-5w-d(EahK z8t9y~$xGCfCvR`!L#nufYqh+7+^~5Kht5@)YSN*h)x`DnH2(d3L2sW;wfqGnGYbFG z@q2CCK9PC9u_XP%+;pt&#c0$<<{}V*mI&c>o4#`wuiq$=`os#J-P48IDqiP5T+@4V z10(4-7~Y<9y7u>2rjW*yjI38yu`NhoUd^~DC)!c{^NVw5vf_=Z6XVB+RBM(h22!nO zF&&jDYvHxqwJW~X^$ha2Vp5{2F#1b`qV?f9XzefvU^6g4mHT~os)ZOn6qN8tRUiy}lm0{AL`BihOD=QWIBS~ZTfFOBgk;MTq?<*mIW@qlsUoGjn zc@_^qal_5BiSpQ58RkO@cSJACY0h0Lpl^n$WM3+XpsC(D+L*E;%~Sm1llMQnN=O;%`l;-Vv~SpG zua9!K-Op|2PW$N01zm}J)%o%K0T!Xcx%{%@V;y5dhB_)f;Q6;)DD2Y}pm}rs;UY9>( zdneIg{>kp|OS0k{fGFGNMj`|Xp2}RRK;Xg0@CW}1{0F7CoUV;^j($(&ggg`pUzTfPNYW%$B%c8*F@7 z;;2`0)C1MGH(60G?EVb}g_^*C&XVwR3)bOwpb2X=SV;7hvS~A zC~PlYaUEf_S32axe1Y4Cmn08mekp+=8jS&UudH)_r0qSFQ&6Oiv)XhhH4tBIigGc(B+-H>7Tt4- zQ`y02Vu0I2r$IOk)#*bFyJtlaI|Mkd3*;O6^UB;rtLAXEjpV{20Pwtg4j8ArpP+H6 z+!WT-8BcShU`IR1j${)&a(OTDrm6DXd6Ce_Xyl4}qW;oC>EdYKTkvY1SS{57-Fm*N z?0ZT^sTh2c${IgBFL)r}z)fK$UZ&t<0ViqFlspK99M&doYxxSIP zpxe0?1yw5}c8Or#%cE7CGvD+&|EcbCE;<1WZ?(M8s6AFTZZAs5qBUznAN&lc1w8l? z?+A<#_PHKN`sbH{EB^YOtvrB|YC0n2v6fkb?12M}{vN6gQj#__9b@r0IN^*?N-O1H zUa3-r&h(M{3yj{F3ZuB$DR(2gMs`v)Ys+qRzrJ*Igj(Ek7#oJR4=Ks8` zK+x}gJ@UalGQRU8@#W`V9=n|B`ApFUB(2IfB&{B3y!*@zLh!jB;owT@@B0KL96%5O01NXOD5h0*!2WwFfjcg=(;=T=Xn_jj#yErLU<$+Kz$WU(i)R>q#;B+(S5#%^+}7BEj;Q2|GG5yVA_YHQ_r! z#$eVuXuz-CBACA43do!lJs+*zt$#;nLLCjfW*xAi@=|l}!yCbsEa%^@RFz855YOC# zUmpz0D0rCM+9sh_SocY-mR?1FV2S!|u!KRsK|c*$|E*@MErVcT-~*+uFm!{12({*u zYeVs+KHetlb8w7Z4}@J;N^IZfS4QK-kawYUFV?y|3?LaE}k=Y6QuTZ+YSrE zv6dg1u*-TNh~Oc%pG;MnSbnP$~I$AD8$N88!ldvdG# zswa;3dn6z;id!IAmgGpl?LrN2l{vY;)eKSj`Ar&|q->9g`FLPuvCoH>wQ^&ZLsTxWBdPm}&Lz54Q_ zlZT4x9UN}-H&z1BuPEqvul%M^DlvUIl$6_x!d1*)k0>6oePZUr5+%ssW+4@NuE}8X&X`;s0epR z%u@58-F*Q5{9sFt^+fH-O|Nc7YCoYSY&1qHW+KfWdSn7cfM-WVEr}WvXB2kn=Oo!( zNJrNvGRQzu6cr`cEOm2G{XNWMvZXKS0uPv`SYK@N`Aum9m+^QHzRxMI^!g9FWAAT? zjXpqh6#`05uOlWL*!n@&_irqAK6%!s_zn##m{g@tfW(t>fJeASx^`Z^QOM}oD>m|4 z&fy{i)^1$m@q?Ek%-h(ZG|Xy#S*^nDppP)ot)F;n?cjT8|3OOwQU7hrPYK%Jb|$Ep zDxT{Fyl^D(Uf^Xvx;Gqytg0<1tpPj2(L#?tc^(ib^%oA;&{1r$6O6j35xN-HX$D;_ zO%%@uH{9-di6ZR*EW-t@r=d1b=kl37`dh1LXym%BcX`cQ9mkb#F%UtMj|QIiy{HpI z7S@j?!1EK1z=GVDr--N7p~P`Jf0+RcZK*%kr~tX`4o2~?jfnjwuG6X^%m@GQBWqPM zMMMcqVV3V}?K1`006;=}iWDfkSTTITu5(@^#4(LCE>E5+Y1^Z$OARqz?^lI5_=vt2 zrS={j?VWiQ>1Zcgm))1Dg2`!z>-L=mnqGn8Rt z)@p+5`e$RBL^eh%mpgS*6}R! zV(u`)GL%n|1-)^-lrsf$Tuv`t2TW^U8~Y->e*#a$Yj)PH3uIg$RZ<_uEu|RC%p8E= z0Xzf_TE~2f?o!PT2`9h&eIcG0?ml4RoSqdbePSgCBq|PHx{aM#`-e03LN?-=PVoX; zfMYl0@pTz1GZJEV$045-tL2{-0ecj!=cXE#5`^hpdpG@`>xikVYKj=SN4L31KOkQ39ym-iIx?~UvgSsAY&MF6S^|PysY+wQ z!FDB#JzFCl9kx!V1GY~av}yfzmBSZ;aYLD`=H=5 zlLlFOb~NUYw*(;xvyyXp`8PA*Ed(S;a?5C!70~B>$rIvMDl%n+8{#|##wn+V=jvRn zn=6P3h&gSagc)#-(eNv4r`4~U*W0;^SgxQm!m1cA@e^+Xi?$HWv-)EHMeAaVOH#pX z1Tf=)6=R7nfH6t$?%En}CmTT07D?vkcqv%_g>rn4mWdq@Y}1S}8NT0qM)~VH6jBsE zGpm8KXM~S#=D%>jF2?sT=EHCuhjOvtS?CoF%`dM?y`9#-%Hyq9!&*;2RO$TWP@qNb zJ>GIgZIAgcApW!|VVE^_n&~b|=VO_$JoldAI#s+_#AkV|h)Q|PJnrGt>dg!oRQPax zs0PX&>@XCE5)0S4%1eXeX$$K)GCKxU!6GRA_tQo9?xcKgJWm~}L<&gs1bEnOHsTUm zW;e)zhO=-bKg^R3KRwR54q9uJA{0~!3$3hP{*zcuTpjH-7T0*y(C<0CbgS3*EeOjQ zDrr}uprEz&qB_5ob8P!#oYKSAo>xMJEGXpdgV1Vh!deX*I8*(9u$xsgeeHvBooptz z-8K3_X{dl?ml8=^N^vw2c&$Ah+DEbEv$@Qrt`SKer$dFCb7^4KUw=AeQtsfd5`?T^ z>Y#R!_0O1JN*L*JVx|1d8kl1Zr-~lIV>90MUGs75)0I{`c21-xtv1nqcK3gxLN_-$ zhxA9b8Zj3EvsDRwqs?7QnaxG|zf9$00o@=6#P3tf(+j3WXGDNJiz+uBYA1GXj0fFE zyQIk~`A=9?!qDgNqG|e^N~3)Cq58lkv$eCwQPaAQJTA69F$IV>xvTkhlpfC;?q@pN zwD#Vw>p$>|?*R)hPS`k-d~d+f_Xn)=%CyVF z0?^mGaGod1G93srmNd}-#4|`SIMSbt7r^hxY%@$_DGi;*`MlZ})2d4tMi$%!0 zMCTZ#bH|nAauW65c-1YoWrfcVvpV%Wh$r#Yj9MRIwYjc((Wk0M19zHF2P6VDrE&Uw zFQ?Zh4j)7>O@LC_{TYtvW_wg?W)gpg>xA=$z3wYo=jBRA9)Lu=mfye zs3UyHDujFR6Rc3vDoPnU`o%G`E{{2Y$DP8H?82z;CXWA&ossAnrPxz0@M4# z@MUAopUlsIM7=ScoaF;6E|2su_1&y@M|?q+OW|8@IhD zK?s2@FPl1~LW69AC3@?*)b`S@0bim2C`?8Ta1sx!68Q-g!^ihxng10Zjuacpc|vyv z*a&Obu42wp=jH~1n?_;Phx)hQ&ddcG6COG(dTiO!m7!Y{PA5R;h4CGEN1>Hd1SE}( zz71^-`@BPy<0!GhzzZ`$<>sjmm&n$l^3szK^@H7kNQoi0DCEa2>m~$9_ zO8!4nHxwPu`afp*8}61{i@{?Mp`TYN8aGcRNW~pEqoz9&a+}=0lx(H}{rrA^JkXjh zGJ@KQ1Xs)StKJD;%$`tyRO;MP9MV==*qLiA6AIHXr!-z(?);dWY3suunKMEWyaWU7 z&sCE2OJP3Mj8wcDh_`@W?XTm}f#h%I@M!?39|~)3jWJM?mW@$DhfICLvodK`(w~yveI%-JM{P=hgD0yf4)leG5q~giR;*rI94^`kJ^aRFF|~*nw^RpUaLHJHMbQfcpx2*>`JiE6 zf+yy2KX#uthH{KGe>8p2^$X;Rk8VBy5{tg!v))}`CQVkp=Nv$-nMwg_G`AhdY)+Fn z+&37pt5du_>_KRyt=;ozhu#%$pgOu$zQ(}bT&6n1kA{9f4ZWEyemr7;6-DQ+9>_*J zOr2c=kZxw)ZpnKDW;dK^?g_nTTlgj;C;DwUf}LbW}(tk3+Ale(6FJxl+cB61dI z;FYo}&z@8MFJ3i(2Mwl|$6=-PWYa92L&+DAFy@?R?wFz|y zG7GS`7~$F6QorSN`fhoXTdx(3w<%);5 z`HQ(P!=xw5Nae$;J<|oh+AUk%<@!F)h&+TLw?xLeI(ppSv;Gm+4REUpAgT=+bLU`0 zO?ma&ccrh7ob{6%tpM8>G>CXCjS5d0sp^xD6>dl45?qf2$TkiCi2cCri?kYzKOan(@yHzPNL{4^ULa)~{l_SZMWR0Q zW%eNUyGa5bJpIaNgl0c>`q=?>d+S7qs3n^iCkjt0^{6k9&==S79%k!7=VzQMKT693 zz9%qM{yGukh+dg6i&0w6_jKn()kx467}^$e*!Dmd7OLK`Hog=I z5>?*~45u{uPbBC*zikmp+@tf;(EIm!r+vgcxS0CU5iu&F&qRjwtba}I44s00AnaNG z2Vwgwlk{eLv347I*>1+D|N$(6y4WT1fv!R=drV&a5JlB^TKr z-t~DpD)hGjVtt_WIN;pN?jq3o4CE{X7C2zW@JMh( zhA@L5^BIG6fNY98o#81=h;+yxYFE0x7d>*7KK`hy<$khHn{?UkQ16#^242U#j>5D~ z$VxJ~%f>2zYO1TO>`~qKfgfD&#T3+O{&&gLiS$%}$cqyUc$(&){Ntc8#5ydU3x1yHE80Kb$Jsb-J zyT1O>tNAQn{{c}y{dp%rUKG42eF|_IixT0IjL-uMf8h_TJj4;lyyd4!nD7C7v@$7l z-(|m_-}>fN7tf`6jiNHszOG7H={HzeJ=`s7f#QMll?RLAgR?B zW}(rJ^$icbBQ#s6Oq8>;Za1&eyXp^n53Y!B|%08k89Zymh?n)jC>c7YbI zj(#R)H9)7y*J-^cxB;h%`Ktv^!E?)oVX|S~-1Ju>1@ce^Yv}#7{Absibx5w?Mqu`Ec={KIg{dbjy`jH4*gaS_vPg zvCJ$4vS%=B%t2)$JiDU%XB|{^;in!lL)L3-u^GGhTyjx`Yl6`e$7~xYQYOA5zDs{N za&gh^Anrv5&8Q@0X!uKhgt8RO&vLR#X4x9M-rYxfb7ryqig%OiPdBoL4jwR73yJ+) z;#_@log}YJS&bZhzj@c_)|}E2V^^IdL-11D9TUZnN$@q;4JzqNWK*J|R$+Nf?^s?w zLtfIfW1rO6MaKJ|WE$6v+RSulthR@LkDQbLinSDm7s=qKdV(5{uWxym`m zD$RFb)sNypQvi$r@}2DSa4;lUEvxQi4Aex~|I6zUR)B@U-1GSg$qIHm<00JCY`}Yo$+j?)`mUbn6VX0$%L=q!eg_Yoh`2exHhK5XO?_!6Mah-Ej z%TnjfzWPc7&7Z{`bT~~AY%~$WbM#R;B~vH)g)uH%9m$lT%^*ieG1Ig1hxJFTBHoCu0JIYJUb}P1fI(E<@SG$<3?}(mmeywP*xVSoh`*; zJiJQeq^-Z(*IX6?s8|`Xxu8v7F5qHDg>DD_{~Hc$6??F+k8;#HaYp$&oOeIJV)JJ) z{IOZ95@kz@^?v)_(%L)Q;rwW zz*(<=g*9Eq7UP15-U*g4m~JA= z?gBW-Wze%Z=6h9!;&-8ZQ>tLvl)fxpj5_-6!LZ(ZwJhm<^2ds!lT825JFwF(iK5&b z9MDai{QY#NDuk1E^qjh9CxAe&o=KvHPL)!_SRWl37g8h-{OMiOn(yBW6aeNIgdH`H zcpSkxKv)x_Yt~{?S29%ZcT;Q}S4&f0oT(fxCA6!CC0-c+61E;_S+)8(PRyL^hKCXU zAQX@3jA9%T<_5SRJB@+^&jFHxiV}NFkeZ8Icq}e}Yp&l4y^2cinP6oE{87eKjTL$5 zVUO6}YZyChy*A}qhb*JUt)ec4@_cZC;_y$dPbl4-Glpc*xIA5fw&uPr1c?UHHDx2@ zwr{+4sQ3biT_E<^@v&5*VOeIdC>S?f=Hcx~T_RNliPc5)3UW%4h_Xn?*Rz)(K*19a z=&hJ4+IiO$kF|ch7+EvknWvvxx!b+E%|)zU)jFFtg5JRc5OABqwT_F%>=JvX?P zE);r)BA=ewdiluK%cBuLp9RiEkXFixltrDQ8vv$tJmc5t5O9W*8bGSDCIcJ^_} zw{*N}r0=?*0@CN+$*munZ#wAJ)n`XjoV?-@AGWmoAy~{jXaqvc1rD{Iy_Fn(%?^4( zBITy&*Vl``>s?SpnCJ{J2QTQ$Hl<5wj$1BEeRh5cYmfe8XLbIA1y78p|FMDm2WL1# z`M;uBqs^JtqRq$gtNL?U?e$BCH?dr+&<43O>%})SV)Vkur1m7eAZ9YY0GgU5t#tk! zxPJu28?Tmtt)9ktffw4$L~lx zNqJ7VPP9kgo2za&y`NZ@PiL1-SnOSvJfIW2HU`(o@UFBG7DbD#Y4-m2)5^%21O%6g zqv&F3>x=JFxjmzk3GlV{`EPnvWZmw%We3yes|YO*^h~_JC|JF>LU-vvnr_3QH2r2$ z*d0q#%^mQ(Ll?+z{4I?rj11zec2#^uMC%q}2=6w|^0;^9I>SxK3Ek0u7HI zCy_~=U51NSpA~j&|2aXCu$MZFAR2MXARKWz)DJ+%1#HI~g6sSI=Ay_;ODmLUm{XHS z9TLVe^lxkXLBX{1sR-nza`_2OeyPmZojTE+s%}q%QsFq9_$re2?rHQ0gTufHjOFZt0$yuIc9n?X$ zN}k^&t87OCSgHQ>7gi}4G1u~isgb`)s1-^<2^;GvR-dBWN8iW@u>{l0mtJNCpNgsD zeRs&#Y_KN!HkA^4PpAC4{mI4cPZ}eCb9yVS=H;(tuC^D*fx5MdFFLyb731FKb2fi} z(v1&$_sCgSUK@QF(L=@0b96a1W#Bqg6f%U(tpUDLK<0dYzGg$_94Gk1TpuH5f!`=$ z0t_eTiIx^mcwHS$;Da3z)>y*f-zwiW1}v=TM16Qi+k4jx)j&6TZBCK=J!K ztGMbhL2rd${%*l;?x^fo!;_wC@vy0uQsS||hZw@FoNgtPgtrlZDV|GV5%xE|GEr&+ z*#Q%IGHe1OngE8xIb~PBgisqVha)#H6Wf)nR;eCrp3U2=l(1IA1_0h$&Obk8gQ1+U zTpMWb)XZLtFTDAL$f@IxnD$32h=lrg9Q_v}=)YQ`{c|TZjXkh|_tyJDDZC-N%QfgE#T##&rVYEtp8J`mC$3w1r->h`Ro9*h9`RgAzEVYE zKhEZ_tbIO)BFk z97;+HUd~-FT>QBZ^(CgLb`|r53BOz-)6i7_&8fcI~;HPuos2oF)|aEy~4jm-3j8?TI`qe*xjY ziTqv~u>$mx%>CsevFG9qr%NXCx{CFGLYX8K`#e1_Wq2`U)fscHG1Y3HiYq#H<-c~uk44%0ij$4S2l8w z=DAeJGucZQ9kwPx;Ui&bzFt!n%LF79*?(SRlD3#w6$C%k6j}##G%+Y-4+c|aI5&d} zgrBZqr1O4#Jz*KZH*T9zfYceNN`u4ybO)%3Y6_NEDo*auxMB4LX1!^OX+QbZ(RMKf z{{mJh{Qk;q+P61?pPFmP`IzN@Ou>5Sl($4rf-YcgkO=NgPfdsL;Zx7?sMRt9p(Td* zZRx3Hxc^D)*uEmvl0aeyy&tdG}A^+h1h*h>UP0*&> z8CcOVz(^)&;PS(Hnpg)Pu-Kv_V~V<386411mo_hCL*9#UfeEF)W1p8S#Q|OZ2sPqb z6>%ufkgrFa0ha{d5%MR@9K(DE-7~z+#5B4f7=j7DP>LQ}=rxv|0S8~eQ0x4MA!5SP zZ!HIY9HQbsdxIII#x`L?&5+{Vk?;Lb$ZCX~F?O?ywCQTp2z7IK?8H$l zI-WOFc2V&S1hM{duTlvKj&LbcSJ7z+XMlPWmn{E`w-UVWXgw;Yt6OZlIiv1-e?zUOPLTNSC+`qT8qnOv+2~dm4bIW4&nz!Ne%i>Tx(uelYuUQ&Fhykw@ z)xQKhNade@!-tp<+wNx+RH?y3CKL=t1nZn*W7T6Cd)%*Mu6TQfc-X)SBE-4cRLKKB z@!H~{L@Z4x-!|Q_$SF#Ni?~%UWlozaug{-%ZHr%`&_tE6x3AJ#t-i|2FGR@J;rUu} zV$Mi95 z`RASSNY$+eG*MLT!KgJT*xsk^K~w3lff0PC9`gft6&w@rH5cNbUN2D1DgZjs%^=YY zD!0LvsfrF!8T$Ga&73MV(>R0Gn?YpV<})(I!ujA%Ox7a(U8Iz@#t?U#Tilt->3h(8 z?DlGBY7uMD>U6ljQ&!;nKzNYe*xCd^106b2te3qYpLAM7%*V66fSzBvGT9A4F@xbh z>J;GIxmdAdkGVp;->*o zLNklm_vP4MY16x-XMw6l2VxH#2;HI^s{iAG0ECX#Bo9FR8kKN>Cj~eg7sBSG<+vt5 z4FDMjD(4$Pnc!mvLQl!ZyvIa?WYhgmIZ39@RxQoj?H+?YlPzbeoSLOu*_+R2^MPFa9X zqbD+^R(jSy%g^2pqPY@%ga8YwI()SZ%N~5W(XJ6JLqO#E2a$xQ6X3Nb|K&4PGK;TI zj!PGb+s~Jwx0t5IN*+iDuW1DC?;&8CaCKE-<=Cy~3%$SIm>SS|pDDnKZO{wNW?G!K z)w$EZv;)f5t5x_4N6GG4|LmI?+6~AiZrFO(?9#Ln;dN1{c~zz^JGxWYoSvemj1Cco zH@as8ui7zNzhg+6f+Z`mEQ?49koky}9r7m>=p!-7*C~yUn@hn;3;n#5=Xk&`vy_nz z3lc|8sEUPq>&;3B%XHX>vpw(ImV4e@zdS;Gm+DZ`k5(2BICPo;e z!5>%^j5_9%xFQ4(0E++>OV2KdNz4wo^`lh5-BHla;LoDC85kRBHKY+Dsg%2%O>PC4VEEIj*}k8P;R0mSGWR5MR&OErOXaij zGvW79nv{4`aH)Vtt~gX9Lb=&sQjy;>9HEs%4_RMz&LmADqP+{cjDO@R%7$B9y`7xf z1-JxLPibaEzP5m1FZ4@N(g`MZJ!g4f-rjq$vwNCZ(Y&Wb_1 zfu%5;>-B>{OxP-X(6sngQz~Zjo+F0Or6w6OI5z~i0L?rpmZp%WqxvE;?AlxVm2($D=3|~t(tfd}sD2cM3 zhz|^a4-^pX>uV^y@56bP%B+IhKvR2i?`65)^Y?58__f+2Q+~s0Vpn1jdYv#|pb|Xn zhvY)m499oNvJdaqp@hGx*-RTtKjvw2!FM6~OmIK3xVL378SCiH^aW&0b20IXaX0Wc$iez6Synp|u7UQjac8X04h9|s(SlzJ?GmIr0^%*@f-c=uj# zLHs6aSarvQG7bUo?`3Avs=GGmY3u^Ns?@AFn6rGvlHI@ND2QaIo-PtR2;Nct{hnQl z19t80`F=gyGyiA-AXjJSF*lF{ql0b0`AH>)*oH3B=E&b)mrT52ql)YU`nUu*yzZkY zDS-7N*KobVjXM&o!73nHb^o^BnTogXF=88xgok2!SC*En>fZ2+iWVofzyLl63-c6Z zTmuk32msf(+R?oL3YjYp9dxI7*NT^H@EY0Zu$pPUJohVr@J@ZK7Nqw~rJhiD{F<~LQ2|Jik%Jigd)LPPxNn-Jh|oPyLZb$(V& z^ei6q%}JLO) z603gqA9!-Opk*(iqPgx|0m@Y&G?f!v^Ea@OBFHMj*Sr|G_E#m`Y35`I_$9V{1g8G| zo&(3pPc8Z6ztzIMpe-n@+_<#baEJIKctzTm+%tp=C3PJNUHSOU!WyA#S?AX^qvX_o z4?qbG^PgUgb@fpqt@X(_#wz>u#L8Y)tT>oZ*5FZ`h>~nkVf>s}0~5TYAR6sKE}>mf z-MqF3x-m#iNrTFBaWNpo1vQZRN!d?BmTlxQ%5O)K8VVqYCp<<)gQ#%P8C(Ij115a` zE_`1cY4o_~xXd)}Of-iV! z&epR2I4>Z(hciCBSWPx8K-(awyFM%2d~ZGxb&IQEueEq08;(VaWP8fslIf4F z0#up5Y-v|UUbn#%Z%)BmDe^^JS$^Ad5sv_K8e4|Mknk{GN|nf(EXW!^4F#9k`;Kw% zN*@YFeV!l?n0Vp~H=)D)Gff?JHh4UiiZ{aqalX@^?1ea6xBXRhyvPJ5EblFWJhxP( zN~Y|1l6L3sV*u3E#eeQS7%mZqm8-4yNyYK6au#pJp|_}ClT`mTMa$0D<}!8*IzE5V z&j%Xm$9tm-Qh~K1F;tR^Y=pEb@0ekgdO1Dk!HDdKonPpN>M1yUH~S)fFr@0Nz?wk!Om zp0CDNFe}%9Po7cN2oWt4f+*f()x& zn7n;<{aULAGyToRI5FvtLdpCzI^&zAxai%gFGgDJ_+cWo`aaTGh}lW;!g;g1b$t@! z>Y$GNIDbYO^rSAe{9@qW+fB~ed8?x%q@Mev$&x*CHrQvq7cXGNK0H$t5w#UoSYk2= z$IGWWC@;S}nvgNz8PqUug+Jv%uKFNk*4BJTISJzR`f-w<3v?w2V%#t?F+4a8>}oPa z76(;Yq(T0I5%1Xoa)UMxFzGvJ)_|Fk;)6YZ0X9ES$luJI7NpgrzDxJy$x5qj4`vxs z|2C6iQ0EXD07M8I?>lE2>(`ThVn18hU6l2#ofkOtblv7`d!ig1g_In|>y_YCUAm&E zA*hklD9P#ay-7W`q@qt2F(bT|G&`LBiVzXrcKo`wcSaM&gP1j9WjQ8KWP}bg+4NB zR6~k8su$~)IO<^mdsT(bNHSy$yz0^IP!iboA zP0R_(vyNGtj9_us^|bR91}q{AlDhK%^?d0lvjP|%N^uB62Mkv0d{!&OUa1Wo>o!9% z9=3Gw6x+*mS1Xotf=?-YCm!n?UB=Obh5XFk@m9g(92pJZ%@O`m*Y)fk!; z=+9tlY%NtU#=~$*i<&s8SgLs#GmUc<0I3-Sx78FRM6|uM+dLODg;dA zj~K2~YNS@rw)x9#e$(8O-u%;Uqp!RQ?Wdd7&@Iz&brK0sg5|(WXZuOCaE32_qqV}x z+M9F(t5=dp3Qj0M<-=uI&oW}zu3c0_4_H;hy1LjxW#dL)=sjMXFPRc^A|X@%-&EtD z4#Dw_rP%XZax+lmCQk%+o%|0#{`gk+>$K`jfQle>j|Kpku4kihVNV#c?1`Epmb-&UCk5HYIzKHibKo~i1){h zVZD790DV3>Y=8%_E2+m1@u_sWh9bs1>vjf(e9_3bzig&-yC@z+HG#bWV!@2vQ zkcuncrgiZ7;t575B<;-n_$iQ z{-6jPy6ve((i;epOyr3|;tey+&h_vH1dAPj*;57bM6ewQGFV_V9U}{OO1n#I^#6_8 zcL#uWZiPv}V}yNwM`%A>(%?70 z*FcADd zj?yK&{kRpB(>E5SS1^KL{FP{ZOd+LRs!C zIB=-DCaG#M63MNMXhos{n3GC<2HwW$u|sNedArLM;y|C%)4fp)#=D04Eo;Z}3vvKQ zKG_vlZ$o_)t2~Yzoh^J8S$~$?7CRcL+}e3VcmD5nB^JB%6sfXhO+DBEV%T_ zwPk_XVJGfr(>UPW7M)Lor`?u?aa0%D1te*C55 z-{<{-6ac%y=7V% zz5GkdD!C=qQ0O;(-`J6RSy26Uq}O_HiQbd$WMcO!Yw^;BM=>+|I7zFclcQt4YZVDf zB&RE~+fBAYAO3W$zNW*}k%fZfJ;ww1NRmHx`pXQe;yH7C}OuLLxSB!Ez z%F!_0#AiI$13Q!SF75r|E~i6mu8ksG8j`3Cni37f$JPLTTLA-l7yNh!Z{Kxr$Z+gc zh?Mr5ll4|M5l1|e25bExmjeW(HWQ;cZ#1e{h4)HTS0!+_Ng(eTfj7{%7a(}q&Dtnf zA5g2S`74nASJ3!xZ_aGEKWLr*k1V3Y+-YI&WkApnjq)jy4Q9nLn~e;(^TVwa*~ZwB z1_|1R!&ZavTs~^=@8)MGH8pCiReBC30Qwu7SY;=4^$zvf;Vp1_`e9qU+;*YCEw(g0 z&Il}P)gQ3f(g+$qM1Q2L#F1l=V6Gn`q@By%a}TmWUia(9+XnWn55LS=*L&rp_cCVR zPq0h7bCi{C?Dh`{*=$P-UppI||LrLM7F>D4CIfqLWxM{v5)fm%4)H4pE;F&HOVYeW z;PECr(2O<&nx&~KUs)LO4v2xzf%f0mjZpeOzd%0}t~<{Edk#+gxCRvOBweTi;-_yB zpw|?<9BZ^q>+t|+)WJ|+P*1QT3hNLiEr#CTcIdFzN?5M(ek)tcIg2^bH4Cn?43*qv zomBp3Dk+bkPUXdK7frU}@!~h1!n9X3setsUR~yC zlmwQ_zFEVTg@L-qQ2fiX?-HLuox}OtT4J3L(ENfVYTbbFdXnF~CB|jdy`JvTo;Q$-)rMP#ky`BooU?Q;6$X=LI%wSU3Y<`H|oM=f?+b&l?-5M5jvLzZc=l zuC3{o+Z~|9jtncW{!tM%1{eBPsl-p7h^h}34m`CYen)q1kTq2?WZ#w?ce$0Qj8xu~vKW#<-ma&lmov*C?2ocr;l;*QseVUvAO{3qt> z=V{sIYmqxS9d1G$sYOP64|PGiXo{8O)~zhZP11Oyp~6|0ia(s6``x3Ax8wDx%bo? z!k*y5S}KA9S^y;;C8fo9AcDqKp(#ZdEn0YMzYKk`bYwm-okG7vDswG`_mb-8@E-^k zU&hd}=OFsbbG9IZ_3tG?j=8RT_rzNbXk`RM#>bv5KDtTH1){ilLS-LgZN@IwLE7Zu zc)O<(xoxGr>fD;#RCbzzhV@|+Ih518Zu|aXunvs$ph_2W#Kf!L=}JEP5KyIs7O)+= zuPUO$FMASrk}1i{k}3ah9{;~`Xp%}#5fQ{{9;r1yHC%#FC1x3YHZp`M3+sy7-Hm?| z@y;HODbg6dm~_Fv3SO50B9Yq-p-&o)d7cNenQ^vF$GzsX}3Wm9gS5J>l)mk7$ z1AVt6YtY{2ENhU$IVOd7*GT7R$CalU!LDBWcBh1q25i3!c@D{a(qEoVQr-<%Vs*$y zLjX&x62N2EL9^7u#Q*%B=GZ?>GlB#oNjl-XT}yCQh_mSuP}ccl*&RkX3hD=N=UDyg z`M?6)UXncEd-Xq~;Q#%-(=Wg!u#5ie?!WiMb@wfiK-2hDwx1&^L}F(!AjmQxaS^on z^H8)jqp)PA8C8$)55WMC+KQwv4GwmlJ>*+}M@jHW>Dx5BF&A5v0h%8fRkD24_ zw2kI5rsO@dS!PGVg~M@Knf(`wbBu?Er$idDo~5EC!{x%e#$u^8de)~&x~cr~XmrkD^Yfoxfjtz|2l6uz6LW_lTer_;8jZ$G zYg`Xd&g`q(a@lm3Tu4_OHZ-A0Hj9%6D3wSnJxzdp@Bsh+9&#HZm9>VHmxHlFp>Q9#I6(&YWW*TLD29bGTM_2|pf zYJevz9vm#AoQezPZINU1Z4eM*IxeJzx;cerg_e|wCeeIJr42o32c~eMcXs0=wQlqsR$aPWk9D}klZAe=at^^q3k-guZ^SiM3 z&p)c)1BmK_;eSq%|0o*|XswmPinfwLc7Vj`Hak+14hKe9-FdWQ${_I~@9~1~X=yV# zvXasDT`p>>sGGaFNj6HsJP|o-l};~4u0N!wrMo^UNtgFyGpT0?TQxXWfV(5DRKR7F z_@8J+7h6tKEG;jm$!<5jC$>Qsnwb(JH>FEzrG_~g=SybB-j+!XcQM6mRBDq(I>D`C zWE`19xgdVkR6d=g=&iQAl6FbFoSk4(k(CD1XZ!W>L>~k)I-cCkFz9cOJ{^_{SvUKUMYo$EwGYI>XAMejI>mmj(;2>bC$szO76;!} z!?FN2mCY*7?#R}w;|00bI(Nqfq9|_ZoM;5%=AOvfJrvbAcFrpuY>I!&K8g;Qit)rE zTv{Ur&>EHzBNroI4c*q0bTXq&=BQ9Q@qUbcy=_y}$DcH!_Q@0S6@E-sP%ZK|ndaY^ zvJ!vzR7-m%GUCa-rU;PJKJC1QoK+N^p#TrT5I#F4rr7vU(^;|CxO|=kSjM@hwyedN&oHHtxkhie$MLJKgdLX z+=`f#N(Yf90Xw-Oo2=9lRKhBny-gP08frxw1uS}qh#~okjQs9EC&~5N%mrz%Fp*J% z<7cSF`?sC>B2QLzh0YKX@w2`OQjgAcdJ>W{_JL zQXLJ0?Gi-y48#yAgttu5OJ3VdM(tuG_u%}L+7ETQ4tb!bDPCjgmsgt&>C@<`y8t3^ z`)`8aeh>s>O=#bOgL=u9H4`jb^B`l8_8zw!qSm0&XA?_<6@JOtv-G3-Xj8;Cgb8!% zq2rq^_w3iXM;c{HQ)+E$lC%4{JyIz3t3$d|faHXx^vzjjl6RE~ntO}rdtfLN2E|Rs z^*665?Lp{r0o!)W;QCi{UCNma*Y>G~FRClYhv{~Q=dx?yRkGeV+IaXW?%2BXW$N<* z_UQx3(&c&sqL>IC#A+Mc7;d`jyc8`JT#gXUI3&3})fB>Uv@`MW>zk2*tbQ)ge(;k8 zBd;zMwWQ}#Ree8YFZS>9-$}6}Yl&e0k>lduu1JH~%OH^^e()7wbppDdC2F3*@qnmk zUJyk)u4i%NQ{pEpV1QMy5*Da@?`d!r1j(1422D@_y)rp+k{a%+4$FnoJlPb^`OFuE z+WMT6xUi5t(V!6u_oL$3S?$cTL%WQ$-E^RZThApPjtD@t%J=H}1wx;*3XM>q{a7jJ zi6`q+BlTxqYP`0HmkBGDrA&RUNxs@FKM=&`*Xy7&$8veiVn{o zOVtA$eohDB_RBG*vST!)+Rg~MaY(yW-@^!I{U$ou15N3fR>+aica|G?T)uH~?)L*Y z)Z)*jYADT7cy^)it`<~^pJG z)l+F|Z**!*uE+EFOp0oK9&je_Wy_ofsL0AaDX@(BzbC#w|I)S{EODMwVSNh_@zjlP zy~@1x{#a!&5Bg853@7jhYsb8{2CQnA-U`QoE`X-4qd`$6yjD)pSnll@PMs?%Ow;Iz zPb(toeSX*~#~!C&oE3Cw1RK%%0A}X+=D^C6LgM+&N7DE)r^j}nZt2n@-E+F~ynU7L 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

EB-@@(EMlhi$ID{=5EsQB86`F!JZ)9t+ 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 G{eJ<>@=?41 From e13ff1d6884ea22d9dd8c356581e69bfea694b30 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Wed, 4 Jun 2025 21:53:51 +0200 Subject: [PATCH 21/91] Fix scenario runner build --- rust/cli/src/lib.rs | 5 ++++- rust/server/src/lib.rs | 9 ++++----- rust/server/src/plugins/js.rs | 18 +++++++++--------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/rust/cli/src/lib.rs b/rust/cli/src/lib.rs index 0c5bef1..2d597a2 100644 --- a/rust/cli/src/lib.rs +++ b/rust/cli/src/lib.rs @@ -75,7 +75,10 @@ pub fn init() { } } - start(cli.minimized) + start( + #[cfg(not(feature = "scenario_runner"))] + cli.minimized, + ) } Some(command) => { match command { diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index a32610d..7a5cbc9 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -3,11 +3,9 @@ use std::fs::File; use std::io::Write; use std::process::exit; use std::sync::Arc; -use std::thread; use std::time::SystemTime; use std::time::UNIX_EPOCH; -use gauntlet_client::open_window; use gauntlet_client::start_client; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::EntrypointId; @@ -38,7 +36,7 @@ pub(crate) mod search; const PLUGIN_CONNECT_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_CONNECT__"; const PLUGIN_UUID_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_UUID__"; -pub fn start(minimized: bool) { +pub fn start(#[cfg(not(feature = "scenario_runner"))] minimized: bool) { register_panic_hook(std::env::var(PLUGIN_UUID_ENV).ok()); if let Ok(socket_name) = std::env::var(PLUGIN_CONNECT_ENV) { @@ -60,7 +58,7 @@ pub fn start(minimized: bool) { #[cfg(not(feature = "scenario_runner"))] { if is_server_running() { - open_window() + gauntlet_client::open_window() } else { let (frontend_sender, frontend_receiver) = channel::(); let (backend_sender, backend_receiver) = @@ -146,6 +144,7 @@ fn run_scenario_runner() { } } +#[cfg(not(feature = "scenario_runner"))] fn is_server_running() -> bool { tokio::runtime::Builder::new_current_thread() .enable_all() @@ -184,7 +183,7 @@ fn start_server( fn start_mock_server( request_sender: RequestSender, backend_receiver: RequestReceiver, - theme: UiTheme, + theme: gauntlet_common::model::UiTheme, ) { tokio::runtime::Builder::new_multi_thread() .enable_all() diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 766f7ef..0c05d5a 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -49,8 +49,6 @@ use serde::Deserialize; use serde::Serialize; use tokio::sync::Mutex; -use crate::PLUGIN_CONNECT_ENV; -use crate::PLUGIN_UUID_ENV; use crate::model::IntermediateUiEvent; use crate::plugins::binary_data_gatherer::BinaryDataGatherer; use crate::plugins::clipboard::Clipboard; @@ -287,14 +285,16 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run stderr_file, }; - let current_exe = std::env::current_exe().context("unable to get current_exe")?; - #[cfg(not(feature = "scenario_runner"))] - let mut runtime_process = std::process::Command::new(current_exe) - .env(PLUGIN_CONNECT_ENV, name_str) - .env(PLUGIN_UUID_ENV, plugin_uuid.clone()) - .spawn() - .context("start plugin runtime process")?; + let mut runtime_process = { + let current_exe = std::env::current_exe().context("unable to get current_exe")?; + + std::process::Command::new(current_exe) + .env(crate::PLUGIN_CONNECT_ENV, name_str) + .env(crate::PLUGIN_UUID_ENV, plugin_uuid.clone()) + .spawn() + .context("start plugin runtime process")?; + }; // use only for debugging and scenario_runner, only works if only one plugin is enabled #[cfg(feature = "scenario_runner")] From f7d7022000fa041c135e372d85bde4b0f786ff96 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Wed, 4 Jun 2025 22:14:46 +0200 Subject: [PATCH 22/91] Fix build --- rust/server/src/lib.rs | 2 +- rust/server/src/plugins/js.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 7a5cbc9..1f9e4d7 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -64,7 +64,7 @@ pub fn start(#[cfg(not(feature = "scenario_runner"))] minimized: bool) { let (backend_sender, backend_receiver) = channel::(); - thread::Builder::new() + std::thread::Builder::new() .name("gauntlet-server".to_string()) .spawn(|| { start_server(frontend_sender, backend_receiver); diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 0c05d5a..f90a249 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -293,7 +293,7 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run .env(crate::PLUGIN_CONNECT_ENV, name_str) .env(crate::PLUGIN_UUID_ENV, plugin_uuid.clone()) .spawn() - .context("start plugin runtime process")?; + .context("start plugin runtime process")? }; // use only for debugging and scenario_runner, only works if only one plugin is enabled From bce1969610c0a2ee7bd59007f65f64bbbfd98eb8 Mon Sep 17 00:00:00 2001 From: Philippe Loctaux Date: Wed, 11 Jun 2025 23:58:33 +0200 Subject: [PATCH 23/91] plugins/gauntlet: applications/macos: follow symlinks inside /Applications Symlinks are not followed by default in function `walk` in the package deno @std/fs --- bundled_plugins/gauntlet/src/applications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundled_plugins/gauntlet/src/applications.tsx b/bundled_plugins/gauntlet/src/applications.tsx index cc1fc18..3019566 100644 --- a/bundled_plugins/gauntlet/src/applications.tsx +++ b/bundled_plugins/gauntlet/src/applications.tsx @@ -196,7 +196,7 @@ export default async function Applications(context: GeneratorContext Date: Sat, 14 Jun 2025 19:36:22 +0200 Subject: [PATCH 24/91] added editorconfig, with yaml --- .editorconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7e464ae --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# EditorConfig is awesome: https://editorconfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.yaml] +indent_style = space +indent_size = 2 From 924fe6906995166eb27846145795bfeae8110aa4 Mon Sep 17 00:00:00 2001 From: Philippe Loctaux Date: Sat, 14 Jun 2025 19:37:55 +0200 Subject: [PATCH 25/91] github actions: formatting: nix with alejandra, remove verbose for rustfmt --- .github/workflows/format.yaml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/format.yaml b/.github/workflows/format.yaml index 0aa561f..9e88d52 100644 --- a/.github/workflows/format.yaml +++ b/.github/workflows/format.yaml @@ -1,12 +1,19 @@ name: format on: [push, pull_request] jobs: - all: + rust: runs-on: ubuntu-latest steps: - uses: dtolnay/rust-toolchain@nightly with: components: rustfmt - uses: actions/checkout@v4 - - name: Check format - run: cargo +nightly fmt --all -- --check --verbose + - name: rustfmt + run: cargo +nightly fmt --all -- --check + nix: + runs-on: ubuntu-latest + steps: + - uses: cachix/install-nix-action@v31 + - uses: actions/checkout@v4 + - name: alejandra + run: nix shell nixpkgs#alejandra -c alejandra -c . From f2fadf8b700a72cc8070c7a91c46e83759e615a4 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 8 Jun 2025 13:49:31 +0200 Subject: [PATCH 26/91] Update iced to latest master. Remove iced_aw, iced_table dependencies. Remove DatePicker component --- Cargo.lock | 919 +++++++++-------- Cargo.toml | 31 +- dev_plugin/src/form-view.tsx | 12 - docs/js/components/date_picker/description.md | 1 - docs/js/components/date_picker/props/label.md | 1 - .../components/date_picker/props/onChange.md | 1 - docs/js/components/date_picker/props/value.md | 1 - example_plugins/plugins/ui_form/gauntlet.toml | 9 - .../plugins/ui_form/src/date-picker.tsx | 17 - example_plugins/plugins/ui_form/src/main.tsx | 7 - js/api/src/gen/components.tsx | 19 +- rust/cli/Cargo.toml | 8 + rust/{server => cli}/build.rs | 0 rust/cli/src/lib.rs | 98 +- rust/client/Cargo.toml | 3 +- rust/client/src/global_shortcut.rs | 238 ----- rust/client/src/lib.rs | 67 +- .../src/ui/custom_widgets/loading_bar.rs | 25 +- rust/client/src/ui/hud/mod.rs | 1 + rust/client/src/ui/mod.rs | 625 +++++------- rust/client/src/ui/sys_tray.rs | 4 +- rust/client/src/ui/theme/button.rs | 32 +- rust/client/src/ui/theme/container.rs | 8 + rust/client/src/ui/theme/date_picker.rs | 84 -- rust/client/src/ui/theme/grid.rs | 2 +- rust/client/src/ui/theme/mod.rs | 74 +- rust/client/src/ui/theme/pick_list.rs | 4 +- rust/client/src/ui/theme/rule.rs | 3 + rust/client/src/ui/theme/scrollable.rs | 4 +- rust/client/src/ui/theme/text_input.rs | 2 +- rust/client/src/ui/widget/data.rs | 13 - rust/client/src/ui/widget/events.rs | 53 - rust/client/src/ui/widget/form.rs | 40 - rust/client/src/ui/widget/grid.rs | 25 +- rust/client/src/ui/widget/images.rs | 452 ++++----- rust/client/src/ui/widget/inline.rs | 7 +- rust/client/src/ui/widget/metadata.rs | 11 +- rust/client/src/ui/widget/root.rs | 6 +- rust/client/src/ui/widget/state.rs | 43 - rust/common/Cargo.toml | 1 + rust/common/src/cli.rs | 109 ++ rust/common/src/lib.rs | 1 + rust/common/src/model.rs | 4 - rust/common/src/rpc/backend_api.rs | 9 - rust/common/src/rpc/frontend_api.rs | 10 - rust/common_ui/Cargo.toml | 1 - rust/common_ui/src/lib.rs | 18 +- rust/component_model/src/lib.rs | 56 +- rust/management_client/Cargo.toml | 2 - rust/management_client/src/components/mod.rs | 1 + .../src/components/shortcut_selector.rs | 37 +- .../src/components/spinner.rs | 208 ++++ rust/management_client/src/theme.rs | 17 +- rust/management_client/src/theme/container.rs | 13 +- .../src/theme/number_input.rs | 55 - rust/management_client/src/theme/pick_list.rs | 4 +- rust/management_client/src/theme/rule.rs | 1 + .../management_client/src/theme/scrollable.rs | 4 +- rust/management_client/src/theme/table.rs | 56 - .../management_client/src/theme/text_input.rs | 4 +- rust/management_client/src/ui.rs | 50 +- rust/management_client/src/views/plugins.rs | 20 +- .../src/views/plugins/preferences.rs | 98 +- .../src/views/plugins/table.rs | 954 +++++++++--------- rust/plugin_runtime/src/lib.rs | 5 +- rust/plugin_runtime/src/ui.rs | 3 +- rust/scenario_runner/src/lib.rs | 2 - rust/server/Cargo.toml | 5 +- rust/server/src/lib.rs | 254 +---- rust/server/src/plugins/config_reader.rs | 2 +- rust/server/src/plugins/data_db_repository.rs | 17 +- rust/server/src/plugins/js.rs | 2 + rust/server/src/plugins/loader.rs | 12 +- rust/server/src/plugins/mod.rs | 209 ++-- rust/server/src/plugins/settings.rs | 420 -------- .../src/plugins/settings/global_shortcut.rs | 649 ++++++++++++ rust/server/src/plugins/settings/mod.rs | 258 +++++ rust/server/src/rpc.rs | 26 +- rust/server/src/search.rs | 2 +- 79 files changed, 3188 insertions(+), 3361 deletions(-) delete mode 100644 docs/js/components/date_picker/description.md delete mode 100644 docs/js/components/date_picker/props/label.md delete mode 100644 docs/js/components/date_picker/props/onChange.md delete mode 100644 docs/js/components/date_picker/props/value.md delete mode 100644 example_plugins/plugins/ui_form/src/date-picker.tsx rename rust/{server => cli}/build.rs (100%) delete mode 100644 rust/client/src/global_shortcut.rs delete mode 100644 rust/client/src/ui/theme/date_picker.rs create mode 100644 rust/common/src/cli.rs create mode 100644 rust/management_client/src/components/spinner.rs delete mode 100644 rust/management_client/src/theme/number_input.rs delete mode 100644 rust/management_client/src/theme/table.rs delete mode 100644 rust/server/src/plugins/settings.rs create mode 100644 rust/server/src/plugins/settings/global_shortcut.rs create mode 100644 rust/server/src/plugins/settings/mod.rs diff --git a/Cargo.lock b/Cargo.lock index c88d042..3bff76e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,6 +140,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + [[package]] name = "aligned-vec" version = "0.5.0" @@ -268,15 +274,6 @@ dependencies = [ "backtrace", ] -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - [[package]] name = "arbitrary" version = "1.4.1" @@ -290,14 +287,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70" dependencies = [ "clipboard-win", - "image 0.25.6", + "image", "log", "objc2 0.6.1", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-core-graphics", "objc2-foundation 0.3.1", - "parking_lot 0.12.3", + "parking_lot", "percent-encoding", "windows-sys 0.59.0", "wl-clipboard-rs", @@ -357,6 +354,24 @@ dependencies = [ "libloading 0.8.8", ] +[[package]] +name = "ashpd" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3d60bee1a1d38c2077030f4788e1b4e31058d2e79a8cfc8f2b440bd44db290" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.8.5", + "serde", + "serde_repr", + "url", + "zbus 5.7.1", +] + [[package]] name = "asn1-rs" version = "0.5.2" @@ -414,12 +429,23 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener", + "event-listener 5.4.0", "event-listener-strategy", "futures-core", "pin-project-lite", ] +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + [[package]] name = "async-channel" version = "2.3.1" @@ -471,6 +497,21 @@ dependencies = [ "futures-lite", ] +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + [[package]] name = "async-io" version = "2.4.1" @@ -496,11 +537,22 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + [[package]] name = "async-once-cell" version = "0.5.4" @@ -513,14 +565,14 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" dependencies = [ - "async-channel", + "async-channel 2.3.1", "async-io", "async-lock", "async-signal", "async-task", "blocking", "cfg-if", - "event-listener", + "event-listener 5.4.0", "futures-lite", "rustix 1.0.7", "tracing", @@ -555,6 +607,32 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "async-std" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -971,7 +1049,7 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ - "async-channel", + "async-channel 2.3.1", "async-task", "futures-io", "futures-lite", @@ -1055,12 +1133,6 @@ dependencies = [ "allocator-api2", ] -[[package]] -name = "by_address" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" - [[package]] name = "bytemuck" version = "1.23.0" @@ -1201,9 +1273,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" dependencies = [ "serde", ] @@ -1319,12 +1391,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "cfg_aliases" version = "0.2.1" @@ -1339,10 +1405,8 @@ checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", - "js-sys", "num-traits", "serde", - "wasm-bindgen", "windows-link", ] @@ -1689,18 +1753,18 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.12.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2" +checksum = "da46a9d5a8905cc538a4a5bceb6a4510de7a51049c5588c0114efce102bcbbe8" dependencies = [ "bitflags 2.9.1", "fontdb 0.16.2", "log", "rangemap", - "rayon", "rustc-hash 1.1.0", "rustybuzz", "self_cell", + "smol_str", "swash", "sys-locale", "ttf-parser 0.21.1", @@ -1891,6 +1955,18 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +[[package]] +name = "cryoglyph" +version = "0.1.0" +source = "git+https://github.com/iced-rs/cryoglyph.git?rev=a456d1c17bbcf33afcca41d9e5e299f9f1193819#a456d1c17bbcf33afcca41d9e5e299f9f1193819" +dependencies = [ + "cosmic-text", + "etagere", + "lru", + "rustc-hash 2.1.1", + "wgpu", +] + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -1975,7 +2051,21 @@ dependencies = [ "rust-ini 0.18.0", "web-sys", "winreg 0.10.1", - "zbus", + "zbus 4.4.0", +] + +[[package]] +name = "dark-light" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e1a09f280e29a8b00bc7e81eca5ac87dca0575639c9422a5fa25a07bb884b8" +dependencies = [ + "ashpd", + "async-std", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "web-sys", + "winreg 0.52.0", ] [[package]] @@ -2023,7 +2113,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.10", + "parking_lot_core", ] [[package]] @@ -2170,7 +2260,7 @@ dependencies = [ "indexmap 2.9.0", "log", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "serde", "serde_json", "sha2", @@ -2188,7 +2278,7 @@ dependencies = [ "bytemuck", "deno_core", "deno_error", - "image 0.25.6", + "image", "lcms2", "num-traits", "thiserror 2.0.12", @@ -2252,7 +2342,7 @@ dependencies = [ "futures", "indexmap 2.9.0", "libc", - "parking_lot 0.12.3", + "parking_lot", "percent-encoding", "pin-project", "serde", @@ -2513,7 +2603,7 @@ dependencies = [ "log", "once_cell", "os_pipe", - "parking_lot 0.12.3", + "parking_lot", "pin-project", "rand 0.8.5", "tokio", @@ -2898,7 +2988,7 @@ dependencies = [ "log", "node_resolver", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "serde", "serde_json", "sys_traits", @@ -3052,7 +3142,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47c618b51088b3ac67f15c69b3ed7620ba3a7d495e5a090186df9424b5ab623e" dependencies = [ "futures", - "parking_lot 0.12.3", + "parking_lot", "tokio", ] @@ -3103,8 +3193,8 @@ dependencies = [ "serde_json", "thiserror 2.0.12", "tokio", - "wgpu-core 24.0.5", - "wgpu-types 24.0.0", + "wgpu-core", + "wgpu-types", ] [[package]] @@ -3892,6 +3982,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "event-listener" version = "5.4.0" @@ -3909,7 +4005,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener", + "event-listener 5.4.0", "pin-project-lite", ] @@ -3940,12 +4036,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -[[package]] -name = "fast-srgb8" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" - [[package]] name = "fastdivide" version = "0.4.2" @@ -4112,9 +4202,9 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "font-types" -version = "0.7.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3971f9a5ca983419cdc386941ba3b9e1feba01a0ab888adf78739feb2798492" +checksum = "02a596f5713680923a2080d86de50fe472fb290693cf0f701187a1c8b36996b7" dependencies = [ "bytemuck", ] @@ -4411,10 +4501,16 @@ dependencies = [ "auto-launch", "clap", "gauntlet-client", + "gauntlet-common", "gauntlet-management-client", + "gauntlet-plugin-runtime", "gauntlet-server", + "gauntlet-utils", + "tokio", "tracing", "tracing-subscriber", + "vergen-gitcl", + "vergen-pretty", ] [[package]] @@ -4427,13 +4523,12 @@ dependencies = [ "gauntlet-common", "gauntlet-common-ui", "gauntlet-component-model", + "gauntlet-server", "gauntlet-utils", - "global-hotkey", "iced", - "iced_aw", "iced_fonts", "iced_layershell", - "image 0.25.6", + "image", "itertools 0.13.0", "objc2-app-kit 0.2.2", "once_cell", @@ -4468,6 +4563,7 @@ dependencies = [ "tokio", "tonic", "tonic-build", + "tracing", ] [[package]] @@ -4493,7 +4589,6 @@ version = "0.0.0" dependencies = [ "gauntlet-common", "iced", - "iced_aw", "iced_fonts", ] @@ -4516,9 +4611,7 @@ dependencies = [ "gauntlet-common-ui", "gauntlet-utils", "iced", - "iced_aw", "iced_fonts", - "iced_table", "itertools 0.13.0", "tracing", "tracing-subscriber", @@ -4555,7 +4648,7 @@ dependencies = [ "gauntlet-component-model", "gauntlet-utils", "icns", - "image 0.25.6", + "image", "indexmap 2.9.0", "interprocess", "libc", @@ -4605,9 +4698,8 @@ dependencies = [ "anyhow", "arboard", "bytes", - "dark-light", + "dark-light 1.1.1", "futures", - "gauntlet-client", "gauntlet-common", "gauntlet-common-plugin-runtime", "gauntlet-plugin-runtime", @@ -4615,7 +4707,8 @@ dependencies = [ "gauntlet-utils", "gauntlet-utils-macros", "git2", - "image 0.25.6", + "global-hotkey", + "image", "include_dir", "interprocess", "itertools 0.13.0", @@ -4638,8 +4731,6 @@ dependencies = [ "ureq", "url", "uuid", - "vergen-gitcl", - "vergen-pretty", "walkdir", ] @@ -5028,15 +5119,15 @@ dependencies = [ ] [[package]] -name = "glow" -version = "0.14.2" +name = "gloo-timers" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" dependencies = [ + "futures-channel", + "futures-core", "js-sys", - "slotmap", "wasm-bindgen", - "web-sys", ] [[package]] @@ -5060,18 +5151,6 @@ dependencies = [ "gl_generator", ] -[[package]] -name = "glyphon" -version = "0.5.0" -source = "git+https://github.com/hecrj/glyphon.git?rev=09712a70df7431e9a3b1ac1bbd4fb634096cb3b4#09712a70df7431e9a3b1ac1bbd4fb634096cb3b4" -dependencies = [ - "cosmic-text", - "etagere", - "lru", - "rustc-hash 2.1.1", - "wgpu", -] - [[package]] name = "gobject-sys" version = "0.18.0" @@ -5423,7 +5502,7 @@ dependencies = [ "ipconfig", "moka", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "rand 0.9.1", "resolv-conf", "serde", @@ -5690,44 +5769,31 @@ dependencies = [ [[package]] name = "iced" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" dependencies = [ "iced_core", + "iced_debug", "iced_futures", "iced_renderer", + "iced_runtime", "iced_widget", "iced_winit", - "image 0.24.9", + "image", "thiserror 1.0.69", ] -[[package]] -name = "iced_aw" -version = "0.11.99" -source = "git+https://github.com/project-gauntlet/iced_aw.git?branch=gauntlet-0.13#c5014d7ad426ae14501b289f37ccdb6f6f307127" -dependencies = [ - "cfg-if", - "chrono", - "iced", - "iced_fonts", - "itertools 0.13.0", - "num-format", - "num-traits", -] - [[package]] name = "iced_core" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" dependencies = [ "bitflags 2.9.1", "bytes", - "dark-light", + "dark-light 2.0.0", "glam", + "lilt", "log", "num-traits", - "once_cell", - "palette", "rustc-hash 2.1.1", "smol_str", "thiserror 1.0.69", @@ -5735,17 +5801,64 @@ dependencies = [ ] [[package]] -name = "iced_fonts" -version = "0.1.99" -source = "git+https://github.com/project-gauntlet/iced_fonts.git?branch=gauntlet-0.13#a4957aa4374477a26d9a8034a01b32ad961802c2" +name = "iced_debug" +version = "0.13.99" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" dependencies = [ "iced_core", + "iced_futures", + "log", +] + +[[package]] +name = "iced_devtools" +version = "0.13.99" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +dependencies = [ + "iced_debug", + "iced_program", + "iced_widget", + "log", +] + +[[package]] +name = "iced_exdevtools" +version = "0.13.99" +source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13.1#1f14198177efc2139f09d1b3bd6294c3f4673925" +dependencies = [ + "iced_debug", + "iced_program", + "iced_widget", + "log", +] + +[[package]] +name = "iced_fonts" +version = "0.2.99" +source = "git+https://github.com/project-gauntlet/iced_fonts.git?branch=gauntlet-0.13.1#d8d4a6f256465473d9099b6ec1da2424151d16d2" +dependencies = [ + "iced_core", + "iced_fonts_macros", + "iced_widget", +] + +[[package]] +name = "iced_fonts_macros" +version = "0.2.99" +source = "git+https://github.com/project-gauntlet/iced_fonts.git?branch=gauntlet-0.13.1#d8d4a6f256465473d9099b6ec1da2424151d16d2" +dependencies = [ + "iced_core", + "iced_widget", + "proc-macro2", + "quote", + "syn 2.0.101", + "ttf-parser 0.25.1", ] [[package]] name = "iced_futures" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" dependencies = [ "futures", "iced_core", @@ -5753,13 +5866,13 @@ dependencies = [ "rustc-hash 2.1.1", "tokio", "wasm-bindgen-futures", - "wasm-timer", + "wasmtimer", ] [[package]] name = "iced_graphics" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" dependencies = [ "bitflags 2.9.1", "bytemuck", @@ -5767,10 +5880,9 @@ dependencies = [ "half", "iced_core", "iced_futures", - "image 0.24.9", + "image", "kamadak-exif", "log", - "once_cell", "raw-window-handle", "rustc-hash 2.1.1", "thiserror 1.0.69", @@ -5780,18 +5892,24 @@ dependencies = [ [[package]] name = "iced_layershell" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13#de88e8d3be5e7ebef6d8456ea62eed26e503bf92" +source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13.1#1f14198177efc2139f09d1b3bd6294c3f4673925" dependencies = [ + "enumflags2", "futures", "iced", "iced_core", + "iced_debug", + "iced_devtools", + "iced_exdevtools", "iced_futures", "iced_graphics", "iced_layershell_macros", + "iced_program", "iced_renderer", "iced_runtime", "layershellev", - "thiserror 1.0.69", + "log", + "thiserror 2.0.12", "tracing", "window_clipboard", ] @@ -5799,7 +5917,7 @@ dependencies = [ [[package]] name = "iced_layershell_macros" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13#de88e8d3be5e7ebef6d8456ea62eed26e503bf92" +source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13.1#1f14198177efc2139f09d1b3bd6294c3f4673925" dependencies = [ "darling", "manyhow", @@ -5808,10 +5926,19 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "iced_program" +version = "0.13.99" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +dependencies = [ + "iced_graphics", + "iced_runtime", +] + [[package]] name = "iced_renderer" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -5823,31 +5950,24 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" dependencies = [ "bytes", "iced_core", + "iced_debug", "iced_futures", "raw-window-handle", "thiserror 1.0.69", ] -[[package]] -name = "iced_table" -version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced_table.git?branch=gauntlet-0.13#a5a1095db8c3aeca4ae6ad851a349147e579a1df" -dependencies = [ - "iced_core", - "iced_widget", -] - [[package]] name = "iced_tiny_skia" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" dependencies = [ "bytemuck", "cosmic-text", + "iced_debug", "iced_graphics", "kurbo 0.10.4", "log", @@ -5860,17 +5980,17 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" dependencies = [ "bitflags 2.9.1", "bytemuck", + "cryoglyph", "futures", "glam", - "glyphon", "guillotiere", + "iced_debug", "iced_graphics", "log", - "once_cell", "resvg 0.42.0", "rustc-hash 2.1.1", "thiserror 1.0.69", @@ -5880,12 +6000,13 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" dependencies = [ "iced_renderer", "iced_runtime", + "log", "num-traits", - "once_cell", + "ouroboros", "rustc-hash 2.1.1", "thiserror 1.0.69", "unicode-segmentation", @@ -5894,18 +6015,16 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13#b6fbbc062c4b007acdcd90cbf498a2c3ece9ae25" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" dependencies = [ - "iced_futures", - "iced_graphics", - "iced_runtime", + "iced_debug", + "iced_program", "log", "rustc-hash 2.1.1", "thiserror 1.0.69", "tracing", "wasm-bindgen-futures", "web-sys", - "winapi", "window_clipboard", "winit", ] @@ -6055,24 +6174,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "image" -version = "0.24.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "exr", - "gif", - "jpeg-decoder", - "num-traits", - "png 0.17.16", - "qoi", - "tiff", -] - [[package]] name = "image" version = "0.25.6" @@ -6415,9 +6516,6 @@ name = "jpeg-decoder" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" -dependencies = [ - "rayon", -] [[package]] name = "js-sys" @@ -6548,18 +6646,27 @@ dependencies = [ "smallvec", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "layershellev" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13#de88e8d3be5e7ebef6d8456ea62eed26e503bf92" +source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13.1#1f14198177efc2139f09d1b3bd6294c3f4673925" dependencies = [ "bitflags 2.9.1", "calloop 0.14.2", "calloop-wayland-source 0.4.0", + "log", "raw-window-handle", "tempfile", - "thiserror 1.0.69", - "tracing", + "thiserror 2.0.12", "waycrate_xkbkeycode", "wayland-backend", "wayland-client", @@ -6784,6 +6891,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "lilt" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67562e5eff6b20553fa9be1c503356768420994e28f67e3eafe6f41910e57ad" +dependencies = [ + "web-time", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -6829,6 +6945,9 @@ name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +dependencies = [ + "value-bag", +] [[package]] name = "loom" @@ -7002,21 +7121,6 @@ dependencies = [ "serde", ] -[[package]] -name = "metal" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" -dependencies = [ - "bitflags 2.9.1", - "block", - "core-graphics-types 0.1.3", - "foreign-types 0.5.0", - "log", - "objc", - "paste", -] - [[package]] name = "metal" version = "0.31.0" @@ -7106,7 +7210,7 @@ dependencies = [ "crossbeam-epoch", "crossbeam-utils", "loom", - "parking_lot 0.12.3", + "parking_lot", "portable-atomic", "rustc_version 0.4.1", "smallvec", @@ -7158,27 +7262,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" -[[package]] -name = "naga" -version = "23.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f" -dependencies = [ - "arrayvec", - "bit-set", - "bitflags 2.9.1", - "cfg_aliases 0.1.1", - "codespan-reporting", - "hexf-parse", - "indexmap 2.9.0", - "log", - "rustc-hash 1.1.0", - "spirv", - "termcolor", - "thiserror 1.0.69", - "unicode-xid", -] - [[package]] name = "naga" version = "24.0.0" @@ -7188,7 +7271,7 @@ dependencies = [ "arrayvec", "bit-set", "bitflags 2.9.1", - "cfg_aliases 0.2.1", + "cfg_aliases", "codespan-reporting", "hexf-parse", "indexmap 2.9.0", @@ -7297,7 +7380,20 @@ checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.9.1", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "cfg_aliases", "libc", "memoffset", ] @@ -8116,6 +8212,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "ouroboros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.101", +] + [[package]] name = "outref" version = "0.5.2" @@ -8196,30 +8316,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "palette" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" -dependencies = [ - "approx", - "fast-srgb8", - "palette_derive", - "phf", -] - -[[package]] -name = "palette_derive" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" -dependencies = [ - "by_address", - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "pango" version = "0.18.3" @@ -8270,17 +8366,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.3" @@ -8288,21 +8373,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.10", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -8790,6 +8861,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "version_check", + "yansi", +] + [[package]] name = "profiling" version = "1.0.16" @@ -8939,7 +9023,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" dependencies = [ "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases", "pin-project-lite", "quinn-proto", "quinn-udp", @@ -8979,7 +9063,7 @@ version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases", "libc", "once_cell", "socket2", @@ -9177,9 +9261,9 @@ dependencies = [ [[package]] name = "read-fonts" -version = "0.22.7" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69aacb76b5c29acfb7f90155d39759a29496aebb49395830e928a9703d2eec2f" +checksum = "04ca636dac446b5664bd16c069c00a9621806895b8bb02c2dc68542b23b8f25d" dependencies = [ "bytemuck", "font-types", @@ -9191,15 +9275,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -10258,9 +10333,9 @@ dependencies = [ [[package]] name = "skrifa" -version = "0.22.3" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1c44ad1f6c5bdd4eefed8326711b7dbda9ea45dfd36068c427d332aa382cbe" +checksum = "dbeb4ca4399663735553a09dd17ce7e49a0a0203f03b706b39628c4d913a8607" dependencies = [ "bytemuck", "read-fonts", @@ -10376,7 +10451,7 @@ checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" dependencies = [ "as-raw-xcb-connection", "bytemuck", - "cfg_aliases 0.2.1", + "cfg_aliases", "core-graphics 0.24.0", "drm", "fastrand", @@ -10581,9 +10656,9 @@ dependencies = [ [[package]] name = "swash" -version = "0.1.19" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd59f3f359ddd2c95af4758c18270eddd9c730dde98598023cdabff472c2ca2" +checksum = "f745de914febc7c9ab4388dfaf94bbc87e69f57bb41133a9b0c84d4be49856f3" dependencies = [ "skrifa", "yazi", @@ -11455,7 +11530,7 @@ dependencies = [ "bytes", "libc", "mio 1.0.4", - "parking_lot 0.12.3", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -12235,6 +12310,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "value-bag" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" + [[package]] name = "value-trait" version = "0.10.1" @@ -12450,21 +12531,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "wasm_dep_analyzer" version = "0.2.0" @@ -12475,16 +12541,30 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "wasmtimer" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "waycrate_xkbkeycode" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13#de88e8d3be5e7ebef6d8456ea62eed26e503bf92" +source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13.1#1f14198177efc2139f09d1b3bd6294c3f4673925" dependencies = [ "bitflags 2.9.1", "calloop 0.14.2", + "log", "memmap2 0.9.5", "smol_str", - "tracing", "wayland-backend", "wayland-client", "xkbcommon-dl", @@ -12703,17 +12783,18 @@ checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" [[package]] name = "wgpu" -version = "23.0.1" +version = "24.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f70000db37c469ea9d67defdc13024ddf9a5f1b89cb2941b812ad7cde1735a" +checksum = "6b0b3436f0729f6cdf2e6e9201f3d39dc95813fad61d826c1ed07918b4539353" dependencies = [ "arrayvec", - "cfg_aliases 0.1.1", + "bitflags 2.9.1", + "cfg_aliases", "document-features", "js-sys", "log", - "naga 23.1.0", - "parking_lot 0.12.3", + "naga", + "parking_lot", "profiling", "raw-window-handle", "smallvec", @@ -12721,34 +12802,9 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "wgpu-core 23.0.1", - "wgpu-hal 23.0.1", - "wgpu-types 23.0.0", -] - -[[package]] -name = "wgpu-core" -version = "23.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a" -dependencies = [ - "arrayvec", - "bit-vec", - "bitflags 2.9.1", - "cfg_aliases 0.1.1", - "document-features", - "indexmap 2.9.0", - "log", - "naga 23.1.0", - "once_cell", - "parking_lot 0.12.3", - "profiling", - "raw-window-handle", - "rustc-hash 1.1.0", - "smallvec", - "thiserror 1.0.69", - "wgpu-hal 23.0.1", - "wgpu-types 23.0.0", + "wgpu-core", + "wgpu-hal", + "wgpu-types", ] [[package]] @@ -12760,13 +12816,13 @@ dependencies = [ "arrayvec", "bit-vec", "bitflags 2.9.1", - "cfg_aliases 0.2.1", + "cfg_aliases", "document-features", "indexmap 2.9.0", "log", - "naga 24.0.0", + "naga", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "profiling", "raw-window-handle", "ron", @@ -12774,53 +12830,8 @@ dependencies = [ "serde", "smallvec", "thiserror 2.0.12", - "wgpu-hal 24.0.4", - "wgpu-types 24.0.0", -] - -[[package]] -name = "wgpu-hal" -version = "23.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821" -dependencies = [ - "android_system_properties", - "arrayvec", - "ash", - "bit-set", - "bitflags 2.9.1", - "block", - "bytemuck", - "cfg_aliases 0.1.1", - "core-graphics-types 0.1.3", - "glow 0.14.2", - "glutin_wgl_sys", - "gpu-alloc", - "gpu-allocator", - "gpu-descriptor", - "js-sys", - "khronos-egl", - "libc", - "libloading 0.8.8", - "log", - "metal 0.29.0", - "naga 23.1.0", - "ndk-sys 0.5.0+25.2.9519653", - "objc", - "once_cell", - "parking_lot 0.12.3", - "profiling", - "range-alloc", - "raw-window-handle", - "renderdoc-sys", - "rustc-hash 1.1.0", - "smallvec", - "thiserror 1.0.69", - "wasm-bindgen", - "web-sys", - "wgpu-types 23.0.0", - "windows 0.58.0", - "windows-core 0.58.0", + "wgpu-hal", + "wgpu-types", ] [[package]] @@ -12836,9 +12847,9 @@ dependencies = [ "bitflags 2.9.1", "block", "bytemuck", - "cfg_aliases 0.2.1", + "cfg_aliases", "core-graphics-types 0.1.3", - "glow 0.16.0", + "glow", "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", @@ -12848,37 +12859,27 @@ dependencies = [ "libc", "libloading 0.8.8", "log", - "metal 0.31.0", - "naga 24.0.0", + "metal", + "naga", "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", "ordered-float 4.6.0", - "parking_lot 0.12.3", + "parking_lot", "profiling", "range-alloc", "raw-window-handle", + "renderdoc-sys", "rustc-hash 1.1.0", "smallvec", "thiserror 2.0.12", "wasm-bindgen", "web-sys", - "wgpu-types 24.0.0", + "wgpu-types", "windows 0.58.0", "windows-core 0.58.0", ] -[[package]] -name = "wgpu-types" -version = "23.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068" -dependencies = [ - "bitflags 2.9.1", - "js-sys", - "web-sys", -] - [[package]] name = "wgpu-types" version = "24.0.0" @@ -13443,7 +13444,7 @@ dependencies = [ "block2 0.5.1", "bytemuck", "calloop 0.13.0", - "cfg_aliases 0.2.1", + "cfg_aliases", "concurrent-queue", "core-foundation 0.9.4", "core-graphics 0.23.2", @@ -13519,6 +13520,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "winsafe" version = "0.0.19" @@ -13703,10 +13714,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" [[package]] -name = "yazi" -version = "0.1.6" +name = "yansi" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yazi" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" [[package]] name = "yoke" @@ -13773,7 +13790,7 @@ dependencies = [ "async-trait", "blocking", "enumflags2", - "event-listener", + "event-listener 5.4.0", "futures-core", "futures-sink", "futures-util", @@ -13789,9 +13806,42 @@ dependencies = [ "uds_windows", "windows-sys 0.52.0", "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 4.4.0", + "zbus_names 3.0.0", + "zvariant 4.2.0", +] + +[[package]] +name = "zbus" +version = "5.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener 5.4.0", + "futures-core", + "futures-lite", + "hex", + "nix 0.30.1", + "ordered-stream", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow 0.7.10", + "zbus_macros 5.7.1", + "zbus_names 4.2.0", + "zvariant 5.5.3", ] [[package]] @@ -13804,7 +13854,22 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.101", - "zvariant_utils", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zbus_macros" +version = "5.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.101", + "zbus_names 4.2.0", + "zvariant 5.5.3", + "zvariant_utils 3.2.0", ] [[package]] @@ -13815,14 +13880,26 @@ checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", - "zvariant", + "zvariant 4.2.0", +] + +[[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.10", + "zvariant 5.5.3", ] [[package]] name = "zeno" -version = "0.2.3" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" @@ -13980,7 +14057,22 @@ dependencies = [ "enumflags2", "serde", "static_assertions", - "zvariant_derive", + "zvariant_derive 4.2.0", +] + +[[package]] +name = "zvariant" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d30786f75e393ee63a21de4f9074d4c038d52c5b1bb4471f955db249f9dffb1" +dependencies = [ + "endi", + "enumflags2", + "serde", + "url", + "winnow 0.7.10", + "zvariant_derive 5.5.3", + "zvariant_utils 3.2.0", ] [[package]] @@ -13993,7 +14085,20 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.101", - "zvariant_utils", + "zvariant_utils 2.1.0", +] + +[[package]] +name = "zvariant_derive" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75fda702cd42d735ccd48117b1630432219c0e9616bf6cb0f8350844ee4d9580" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.101", + "zvariant_utils 3.2.0", ] [[package]] @@ -14006,3 +14111,17 @@ dependencies = [ "quote", "syn 2.0.101", ] + +[[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.101", + "winnow 0.7.10", +] diff --git a/Cargo.toml b/Cargo.toml index 5c55a66..6231b8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,16 +25,12 @@ edition = "2024" [workspace.dependencies] # iced -#iced = { version = "0.13.99", features = ["tiny-skia", "wgpu", "tokio", "lazy", "advanced", "image", "web-colors", "svg"] } -iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet-0.13", default-features = false, features = ["tiny-skia", "wgpu", "tokio", "advanced", "image", "web-colors", "svg"] } -#iced_aw = { version = "0.11.99", features = ["date_picker", "wrap", "number_input", "grid", "spinner"] } -iced_aw = { git = "https://github.com/project-gauntlet/iced_aw.git", branch = "gauntlet-0.13", default-features = false, features = ["date_picker", "wrap", "number_input", "grid", "spinner"] } -#iced_table = "0.13.99" -iced_table = { git = "https://github.com/project-gauntlet/iced_table.git", branch = "gauntlet-0.13" } -#iced_fonts = { version = "0.1.99", features = ["bootstrap"] } -iced_fonts = { git = "https://github.com/project-gauntlet/iced_fonts.git", branch = "gauntlet-0.13", features = ["bootstrap"] } +#iced = { version = "0.13.99", features = ["tokio", "lazy", "advanced", "image", "svg"] } +iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet-0.13.1", features = ["tokio", "lazy", "advanced", "image", "svg"] } +#iced_fonts = { version = "0.2.99", features = ["bootstrap"] } +iced_fonts = { git = "https://github.com/project-gauntlet/iced_fonts.git", branch = "gauntlet-0.13.1", features = ["bootstrap"] } #iced_layershell = "0.13.99" -iced_layershell = { git = "https://github.com/project-gauntlet/exwlshelleventloop.git", branch = "gauntlet-0.13" } +iced_layershell = { git = "https://github.com/project-gauntlet/exwlshelleventloop.git", branch = "gauntlet-0.13.1" } # workspaces gauntlet-common = { path = "./rust/common" } @@ -86,3 +82,20 @@ inherits = "release" opt-level = "s" lto = "thin" strip = true + +#[patch.crates-io] +#iced_fonts = { path = "../../../RustroverProjects/iced_fonts_fork" } +#iced = { path = "../../../RustroverProjects/iced-fork" } +#iced_debug = { path = "../../../RustroverProjects/iced-fork/debug" } +#iced_devtools = { path = "../../../RustroverProjects/iced-fork/devtools" } +#iced_program = { path = "../../../RustroverProjects/iced-fork/program" } +#iced_core = { path = "../../../RustroverProjects/iced-fork/core" } +#iced_futures = { path = "../../../RustroverProjects/iced-fork/futures" } +#iced_graphics = { path = "../../../RustroverProjects/iced-fork/graphics" } +#iced_renderer = { path = "../../../RustroverProjects/iced-fork/renderer" } +#iced_runtime = { path = "../../../RustroverProjects/iced-fork/runtime" } +#iced_tiny_skia = { path = "../../../RustroverProjects/iced-fork/tiny_skia" } +#iced_wgpu = { path = "../../../RustroverProjects/iced-fork/wgpu" } +#iced_widget = { path = "../../../RustroverProjects/iced-fork/widget" } +#iced_winit = { path = "../../../RustroverProjects/iced-fork/winit" } +#iced_layershell = { path = "../../../RustroverProjects/exwlshelleventloop/iced_layershell" } diff --git a/dev_plugin/src/form-view.tsx b/dev_plugin/src/form-view.tsx index 1f9ed5b..1352640 100644 --- a/dev_plugin/src/form-view.tsx +++ b/dev_plugin/src/form-view.tsx @@ -64,12 +64,6 @@ export default function FormView(): ReactElement { Select Item 3 Select Item 4 - { - console.log(`uncontrolled value: ${value}`) - }} - /> {/* controlled */} Default Select Item Select Item 4 - { - console.log(`controlled value: ${value}`) - }} - /> ); }; diff --git a/docs/js/components/date_picker/description.md b/docs/js/components/date_picker/description.md deleted file mode 100644 index de8191c..0000000 --- a/docs/js/components/date_picker/description.md +++ /dev/null @@ -1 +0,0 @@ -Date Picker is a type of form input that produces date value represented as a string in "YYYY-MM-DD" format \ No newline at end of file diff --git a/docs/js/components/date_picker/props/label.md b/docs/js/components/date_picker/props/label.md deleted file mode 100644 index 82c1e5d..0000000 --- a/docs/js/components/date_picker/props/label.md +++ /dev/null @@ -1 +0,0 @@ -Text displayed in UI to the left of the date picker itself \ No newline at end of file diff --git a/docs/js/components/date_picker/props/onChange.md b/docs/js/components/date_picker/props/onChange.md deleted file mode 100644 index 0472326..0000000 --- a/docs/js/components/date_picker/props/onChange.md +++ /dev/null @@ -1 +0,0 @@ -Function that is called when the value of the data picker was changed \ No newline at end of file diff --git a/docs/js/components/date_picker/props/value.md b/docs/js/components/date_picker/props/value.md deleted file mode 100644 index 607346c..0000000 --- a/docs/js/components/date_picker/props/value.md +++ /dev/null @@ -1 +0,0 @@ -Value of the Date Picker. Can be used to implement controlled form \ No newline at end of file diff --git a/example_plugins/plugins/ui_form/gauntlet.toml b/example_plugins/plugins/ui_form/gauntlet.toml index f2bed62..047c615 100644 --- a/example_plugins/plugins/ui_form/gauntlet.toml +++ b/example_plugins/plugins/ui_form/gauntlet.toml @@ -12,15 +12,6 @@ type = 'view' description = '' # docs-code-segment:end -# docs-code-segment:start date-picker -[[entrypoint]] -id = 'date-picker' -name = 'Date Picker' -path = 'src/date-picker.tsx' -type = 'view' -description = '' -# docs-code-segment:end - # docs-code-segment:start main [[entrypoint]] id = 'main' diff --git a/example_plugins/plugins/ui_form/src/date-picker.tsx b/example_plugins/plugins/ui_form/src/date-picker.tsx deleted file mode 100644 index 778ea00..0000000 --- a/example_plugins/plugins/ui_form/src/date-picker.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { ReactElement } from 'react'; -import { Form } from "@project-gauntlet/api/components"; - -export default function DatePickerExample(): ReactElement { - return ( -
- { - console.log(`value: ${value}`) - }} - /> - - ); -}; - diff --git a/example_plugins/plugins/ui_form/src/main.tsx b/example_plugins/plugins/ui_form/src/main.tsx index 6afb36f..e82bc5a 100644 --- a/example_plugins/plugins/ui_form/src/main.tsx +++ b/example_plugins/plugins/ui_form/src/main.tsx @@ -30,13 +30,6 @@ export default function MainExample(): ReactElement { - { - console.log(`value: ${value}`) - }} - /> void; }; - ["gauntlet:date_picker"]: { - label?: string; - value?: string; - onChange?: (value: string | undefined) => void; - }; ["gauntlet:select_item"]: { children?: StringComponent; value: string; @@ -112,7 +107,7 @@ declare global { }; ["gauntlet:separator"]: {}; ["gauntlet:form"]: { - children?: ElementComponent; + children?: ElementComponent; isLoading?: boolean; }; ["gauntlet:inline_separator"]: { @@ -585,14 +580,6 @@ export interface CheckboxProps { export const Checkbox: FC = (props: CheckboxProps): ReactNode => { return ; }; -export interface DatePickerProps { - label?: string; - value?: string; - onChange?: (value: string | undefined) => void; -} -export const DatePicker: FC = (props: DatePickerProps): ReactNode => { - return ; -}; export interface SelectItemProps { children?: StringComponent; value: string; @@ -616,7 +603,7 @@ export const Separator: FC = (): ReactNode => { return ; }; export interface FormProps { - children?: ElementComponent; + children?: ElementComponent; isLoading?: boolean; actions?: ElementComponent; } @@ -624,7 +611,6 @@ export const Form: FC & { TextField: typeof TextField; PasswordField: typeof PasswordField; Checkbox: typeof Checkbox; - DatePicker: typeof DatePicker; Select: typeof Select; Separator: typeof Separator; } = (props: FormProps): ReactNode => { @@ -633,7 +619,6 @@ export const Form: FC & { Form.TextField = TextField; Form.PasswordField = PasswordField; Form.Checkbox = Checkbox; -Form.DatePicker = DatePicker; Form.Select = Select; Form.Separator = Separator; export interface InlineSeparatorProps { diff --git a/rust/cli/Cargo.toml b/rust/cli/Cargo.toml index ec7f4fa..fd48cbc 100644 --- a/rust/cli/Cargo.toml +++ b/rust/cli/Cargo.toml @@ -7,14 +7,22 @@ edition.workspace = true gauntlet-management-client.workspace = true gauntlet-client.workspace = true gauntlet-server.workspace = true +gauntlet-plugin-runtime.workspace = true +gauntlet-common.workspace = true +gauntlet-utils.workspace = true # shared +tokio.workspace = true tracing.workspace = true tracing-subscriber.workspace = true anyhow.workspace = true # other clap = { version = "4.5", features = ["derive"] } +vergen-pretty = "0.3" + +[build-dependencies] +vergen-gitcl = { version = "1.0", features = ["build", "cargo"] } [target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies] auto-launch = "0.5.0" diff --git a/rust/server/build.rs b/rust/cli/build.rs similarity index 100% rename from rust/server/build.rs rename to rust/cli/build.rs diff --git a/rust/cli/src/lib.rs b/rust/cli/src/lib.rs index 2d597a2..4a8f0ac 100644 --- a/rust/cli/src/lib.rs +++ b/rust/cli/src/lib.rs @@ -1,9 +1,20 @@ +use std::backtrace::Backtrace; +use std::fs::File; +use std::io::Write; +use std::process::exit; +use std::time::SystemTime; +use std::time::UNIX_EPOCH; + use clap::Parser; -use gauntlet_client::open_window; +use gauntlet_common::cli::is_server_running; +use gauntlet_common::cli::open_window; +use gauntlet_common::cli::run_action; +use gauntlet_common::dirs::Dirs; use gauntlet_management_client::start_management_client; -use gauntlet_server::run_action; -use gauntlet_server::start; +use gauntlet_server::PLUGIN_CONNECT_ENV; +use gauntlet_server::PLUGIN_UUID_ENV; use tracing_subscriber::EnvFilter; +use vergen_pretty::vergen_pretty_env; /// Gauntlet CLI /// @@ -75,10 +86,32 @@ pub fn init() { } } - start( - #[cfg(not(feature = "scenario_runner"))] - cli.minimized, - ) + register_panic_hook(std::env::var(PLUGIN_UUID_ENV).ok()); + + if let Ok(socket_name) = std::env::var(PLUGIN_CONNECT_ENV) { + gauntlet_plugin_runtime::run_plugin_runtime(socket_name); + + return; + } + + tracing::info!("Gauntlet Build Information:"); + for (name, value) in vergen_pretty_env!() { + if let Some(value) = value { + tracing::info!("{}: {}", name, value); + } + } + + #[cfg(feature = "scenario_runner")] + run_scenario_runner(); + + #[cfg(not(feature = "scenario_runner"))] + { + if is_server_running() { + open_window() + } else { + gauntlet_client::run_app(cli.minimized) + } + } } Some(command) => { match command { @@ -143,3 +176,54 @@ fn setup_auto_launch(app_path: String) -> anyhow::Result<()> { Ok(()) } + +fn register_panic_hook(plugin_runtime: Option) { + unsafe { + std::env::set_var("RUST_BACKTRACE", "full"); + }; + + let dirs = Dirs::new(); + + let crash_file = match plugin_runtime { + None => dirs.server_crash_log_file(), + Some(plugin_uuid) => dirs.plugin_crash_log_file(&plugin_uuid), + }; + + let _ = std::fs::remove_file(&crash_file); + + std::panic::set_hook(Box::new(move |panic_info| { + let payload = panic_info.payload(); + + let payload = if let Some(&s) = payload.downcast_ref::<&'static str>() { + s + } else if let Some(s) = payload.downcast_ref::() { + s.as_str() + } else { + "Box" + }; + + let location = panic_info.location().map(|l| l.to_string()); + let backtrace = Backtrace::capture(); + + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .ok() + .map(|duration| duration.as_millis().to_string()) + .unwrap_or("Unknown".to_string()); + + let content = format!( + "Panic on {}\nPayload: {}\nLocation: {:?}\nBacktrace:\n{}", + now, payload, location, backtrace + ); + + let crash_file = File::options().create(true).append(true).open(&crash_file); + + if let Ok(mut crash_file) = crash_file { + let _ = crash_file.write_all(content.as_bytes()); + } + + eprintln!("{}", content); + + exit(101); // poor man's abort on panic because actual setting makes v8 linking fail + })); +} diff --git a/rust/client/Cargo.toml b/rust/client/Cargo.toml index 2cdca65..49fcb05 100644 --- a/rust/client/Cargo.toml +++ b/rust/client/Cargo.toml @@ -8,12 +8,12 @@ gauntlet-common.workspace = true gauntlet-common-ui.workspace = true gauntlet-utils.workspace = true gauntlet-component-model.workspace = true +gauntlet-server.workspace = true # shared tokio.workspace = true anyhow.workspace = true iced.workspace = true -iced_aw.workspace = true iced_fonts.workspace = true tracing.workspace = true itertools.workspace = true @@ -23,7 +23,6 @@ image.workspace = true once_cell.workspace = true # other -global-hotkey = "0.7.0" arc-swap = "1.7.1" [target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies] diff --git a/rust/client/src/global_shortcut.rs b/rust/client/src/global_shortcut.rs deleted file mode 100644 index d02d386..0000000 --- a/rust/client/src/global_shortcut.rs +++ /dev/null @@ -1,238 +0,0 @@ -use gauntlet_common::model::PhysicalKey; -use gauntlet_common::model::PhysicalShortcut; -use global_hotkey::hotkey::Code; -use global_hotkey::hotkey::HotKey; -use global_hotkey::hotkey::Modifiers; -use iced::futures::SinkExt; -use iced::futures::channel::mpsc::Sender; -use tokio::runtime::Handle; - -use crate::ui::AppMsg; - -pub fn register_listener(msg_sender: Sender) { - let handle = Handle::current(); - - global_hotkey::GlobalHotKeyEvent::set_event_handler(Some(move |e: global_hotkey::GlobalHotKeyEvent| { - let mut msg_sender = msg_sender.clone(); - - if let global_hotkey::HotKeyState::Pressed = e.state() { - handle.spawn(async move { - if let Err(err) = msg_sender.send(AppMsg::HandleGlobalShortcut(e.id)).await { - tracing::warn!(target = "rpc", "error occurred when receiving shortcut event {:?}", err) - } - }); - } - })); -} - -pub fn convert_physical_shortcut_to_hotkey(shortcut: PhysicalShortcut) -> HotKey { - let modifiers: Modifiers = { - let mut modifiers = Modifiers::empty(); - - if shortcut.modifier_alt { - modifiers.insert(Modifiers::ALT); - } - - if shortcut.modifier_control { - modifiers.insert(Modifiers::CONTROL); - } - - if shortcut.modifier_meta { - modifiers.insert(Modifiers::META); - } - - if shortcut.modifier_shift { - modifiers.insert(Modifiers::SHIFT); - } - - modifiers - }; - - let code = match shortcut.physical_key { - PhysicalKey::Backquote => Code::Backquote, - PhysicalKey::Backslash => Code::Backslash, - PhysicalKey::BracketLeft => Code::BracketLeft, - PhysicalKey::BracketRight => Code::BracketRight, - PhysicalKey::Comma => Code::Comma, - PhysicalKey::Digit1 => Code::Digit1, - PhysicalKey::Digit2 => Code::Digit2, - PhysicalKey::Digit3 => Code::Digit3, - PhysicalKey::Digit4 => Code::Digit4, - PhysicalKey::Digit5 => Code::Digit5, - PhysicalKey::Digit6 => Code::Digit6, - PhysicalKey::Digit7 => Code::Digit7, - PhysicalKey::Digit8 => Code::Digit8, - PhysicalKey::Digit9 => Code::Digit9, - PhysicalKey::Digit0 => Code::Digit0, - PhysicalKey::Equal => Code::Equal, - PhysicalKey::IntlBackslash => Code::IntlBackslash, - PhysicalKey::IntlRo => Code::IntlRo, - PhysicalKey::IntlYen => Code::IntlYen, - PhysicalKey::KeyA => Code::KeyA, - PhysicalKey::KeyB => Code::KeyB, - PhysicalKey::KeyC => Code::KeyC, - PhysicalKey::KeyD => Code::KeyD, - PhysicalKey::KeyE => Code::KeyE, - PhysicalKey::KeyF => Code::KeyF, - PhysicalKey::KeyG => Code::KeyG, - PhysicalKey::KeyH => Code::KeyH, - PhysicalKey::KeyI => Code::KeyI, - PhysicalKey::KeyJ => Code::KeyJ, - PhysicalKey::KeyK => Code::KeyK, - PhysicalKey::KeyL => Code::KeyL, - PhysicalKey::KeyM => Code::KeyM, - PhysicalKey::KeyN => Code::KeyN, - PhysicalKey::KeyO => Code::KeyO, - PhysicalKey::KeyP => Code::KeyP, - PhysicalKey::KeyQ => Code::KeyQ, - PhysicalKey::KeyR => Code::KeyR, - PhysicalKey::KeyS => Code::KeyS, - PhysicalKey::KeyT => Code::KeyT, - PhysicalKey::KeyU => Code::KeyU, - PhysicalKey::KeyV => Code::KeyV, - PhysicalKey::KeyW => Code::KeyW, - PhysicalKey::KeyX => Code::KeyX, - PhysicalKey::KeyY => Code::KeyY, - PhysicalKey::KeyZ => Code::KeyZ, - PhysicalKey::Minus => Code::Minus, - PhysicalKey::Period => Code::Period, - PhysicalKey::Quote => Code::Quote, - PhysicalKey::Semicolon => Code::Semicolon, - PhysicalKey::Slash => Code::Slash, - PhysicalKey::Backspace => Code::Backspace, - PhysicalKey::CapsLock => Code::CapsLock, - PhysicalKey::ContextMenu => Code::ContextMenu, - PhysicalKey::Enter => Code::Enter, - PhysicalKey::Space => Code::Space, - PhysicalKey::Tab => Code::Tab, - PhysicalKey::Convert => Code::Convert, - PhysicalKey::KanaMode => Code::KanaMode, - PhysicalKey::Lang1 => Code::Lang1, - PhysicalKey::Lang2 => Code::Lang2, - PhysicalKey::Lang3 => Code::Lang3, - PhysicalKey::Lang4 => Code::Lang4, - PhysicalKey::Lang5 => Code::Lang5, - PhysicalKey::NonConvert => Code::NonConvert, - PhysicalKey::Delete => Code::Delete, - PhysicalKey::End => Code::End, - PhysicalKey::Help => Code::Help, - PhysicalKey::Home => Code::Home, - PhysicalKey::Insert => Code::Insert, - PhysicalKey::PageDown => Code::PageDown, - PhysicalKey::PageUp => Code::PageUp, - PhysicalKey::ArrowDown => Code::ArrowDown, - PhysicalKey::ArrowLeft => Code::ArrowLeft, - PhysicalKey::ArrowRight => Code::ArrowRight, - PhysicalKey::ArrowUp => Code::ArrowUp, - PhysicalKey::NumLock => Code::NumLock, - PhysicalKey::Numpad0 => Code::Numpad0, - PhysicalKey::Numpad1 => Code::Numpad1, - PhysicalKey::Numpad2 => Code::Numpad2, - PhysicalKey::Numpad3 => Code::Numpad3, - PhysicalKey::Numpad4 => Code::Numpad4, - PhysicalKey::Numpad5 => Code::Numpad5, - PhysicalKey::Numpad6 => Code::Numpad6, - PhysicalKey::Numpad7 => Code::Numpad7, - PhysicalKey::Numpad8 => Code::Numpad8, - PhysicalKey::Numpad9 => Code::Numpad9, - PhysicalKey::NumpadAdd => Code::NumpadAdd, - PhysicalKey::NumpadBackspace => Code::NumpadBackspace, - PhysicalKey::NumpadClear => Code::NumpadClear, - PhysicalKey::NumpadClearEntry => Code::NumpadClearEntry, - PhysicalKey::NumpadComma => Code::NumpadComma, - PhysicalKey::NumpadDecimal => Code::NumpadDecimal, - PhysicalKey::NumpadDivide => Code::NumpadDivide, - PhysicalKey::NumpadEnter => Code::NumpadEnter, - PhysicalKey::NumpadEqual => Code::NumpadEqual, - PhysicalKey::NumpadHash => Code::NumpadHash, - PhysicalKey::NumpadMemoryAdd => Code::NumpadMemoryAdd, - PhysicalKey::NumpadMemoryClear => Code::NumpadMemoryClear, - PhysicalKey::NumpadMemoryRecall => Code::NumpadMemoryRecall, - PhysicalKey::NumpadMemoryStore => Code::NumpadMemoryStore, - PhysicalKey::NumpadMemorySubtract => Code::NumpadMemorySubtract, - PhysicalKey::NumpadMultiply => Code::NumpadMultiply, - PhysicalKey::NumpadParenLeft => Code::NumpadParenLeft, - PhysicalKey::NumpadParenRight => Code::NumpadParenRight, - PhysicalKey::NumpadStar => Code::NumpadStar, - PhysicalKey::NumpadSubtract => Code::NumpadSubtract, - PhysicalKey::Escape => Code::Escape, - PhysicalKey::Fn => Code::Fn, - PhysicalKey::FnLock => Code::FnLock, - PhysicalKey::PrintScreen => Code::PrintScreen, - PhysicalKey::ScrollLock => Code::ScrollLock, - PhysicalKey::Pause => Code::Pause, - PhysicalKey::BrowserBack => Code::BrowserBack, - PhysicalKey::BrowserFavorites => Code::BrowserFavorites, - PhysicalKey::BrowserForward => Code::BrowserForward, - PhysicalKey::BrowserHome => Code::BrowserHome, - PhysicalKey::BrowserRefresh => Code::BrowserRefresh, - PhysicalKey::BrowserSearch => Code::BrowserSearch, - PhysicalKey::BrowserStop => Code::BrowserStop, - PhysicalKey::Eject => Code::Eject, - PhysicalKey::LaunchApp1 => Code::LaunchApp1, - PhysicalKey::LaunchApp2 => Code::LaunchApp2, - PhysicalKey::LaunchMail => Code::LaunchMail, - PhysicalKey::MediaPlayPause => Code::MediaPlayPause, - PhysicalKey::MediaSelect => Code::MediaSelect, - PhysicalKey::MediaStop => Code::MediaStop, - PhysicalKey::MediaTrackNext => Code::MediaTrackNext, - PhysicalKey::MediaTrackPrevious => Code::MediaTrackPrevious, - PhysicalKey::Power => Code::Power, - PhysicalKey::Sleep => Code::Sleep, - PhysicalKey::AudioVolumeDown => Code::AudioVolumeDown, - PhysicalKey::AudioVolumeMute => Code::AudioVolumeMute, - PhysicalKey::AudioVolumeUp => Code::AudioVolumeUp, - PhysicalKey::WakeUp => Code::WakeUp, - PhysicalKey::Abort => Code::Abort, - PhysicalKey::Resume => Code::Resume, - PhysicalKey::Suspend => Code::Suspend, - PhysicalKey::Again => Code::Again, - PhysicalKey::Copy => Code::Copy, - PhysicalKey::Cut => Code::Cut, - PhysicalKey::Find => Code::Find, - PhysicalKey::Open => Code::Open, - PhysicalKey::Paste => Code::Paste, - PhysicalKey::Props => Code::Props, - PhysicalKey::Select => Code::Select, - PhysicalKey::Undo => Code::Undo, - PhysicalKey::Hiragana => Code::Hiragana, - PhysicalKey::Katakana => Code::Katakana, - PhysicalKey::F1 => Code::F1, - PhysicalKey::F2 => Code::F2, - PhysicalKey::F3 => Code::F3, - PhysicalKey::F4 => Code::F4, - PhysicalKey::F5 => Code::F5, - PhysicalKey::F6 => Code::F6, - PhysicalKey::F7 => Code::F7, - PhysicalKey::F8 => Code::F8, - PhysicalKey::F9 => Code::F9, - PhysicalKey::F10 => Code::F10, - PhysicalKey::F11 => Code::F11, - PhysicalKey::F12 => Code::F12, - PhysicalKey::F13 => Code::F13, - PhysicalKey::F14 => Code::F14, - PhysicalKey::F15 => Code::F15, - PhysicalKey::F16 => Code::F16, - PhysicalKey::F17 => Code::F17, - PhysicalKey::F18 => Code::F18, - PhysicalKey::F19 => Code::F19, - PhysicalKey::F20 => Code::F20, - PhysicalKey::F21 => Code::F21, - PhysicalKey::F22 => Code::F22, - PhysicalKey::F23 => Code::F23, - PhysicalKey::F24 => Code::F24, - PhysicalKey::F25 => Code::F25, - PhysicalKey::F26 => Code::F26, - PhysicalKey::F27 => Code::F27, - PhysicalKey::F28 => Code::F28, - PhysicalKey::F29 => Code::F29, - PhysicalKey::F30 => Code::F30, - PhysicalKey::F31 => Code::F31, - PhysicalKey::F32 => Code::F32, - PhysicalKey::F33 => Code::F33, - PhysicalKey::F34 => Code::F34, - PhysicalKey::F35 => Code::F35, - }; - - HotKey::new(Some(modifiers), code) -} diff --git a/rust/client/src/lib.rs b/rust/client/src/lib.rs index 905dbcd..51449da 100644 --- a/rust/client/src/lib.rs +++ b/rust/client/src/lib.rs @@ -1,65 +1,6 @@ -use gauntlet_common::rpc::backend_api::BackendForCliApi; -use gauntlet_common::rpc::backend_api::BackendForCliApiProxy; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiRequestData; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiResponseData; -use gauntlet_common::rpc::backend_api::GrpcBackendApi; -use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; -use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; -use gauntlet_utils::channel::RequestReceiver; -use gauntlet_utils::channel::RequestSender; +mod model; +mod ui; -pub mod global_shortcut; -pub(crate) mod model; -pub(crate) mod ui; - -pub fn start_client( - minimized: bool, - frontend_receiver: RequestReceiver, - backend_sender: RequestSender, -) { - ui::run(minimized, frontend_receiver, backend_sender); -} - -pub fn open_window() { - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .expect("unable to start server tokio runtime") - .block_on(async { - let result = GrpcBackendApi::new().await; - - match result { - Ok(backend_api) => { - let backend_api = BackendForCliApiProxy::new(backend_api); - - tracing::info!("Server is already running, opening window..."); - - backend_api.show_window().await.expect("Unknown error") - } - Err(_) => { - tracing::error!("Unable to connect to server. Please check if you have Gauntlet running on your PC") - } - } - }) -} - -pub fn open_settings_window() { - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .expect("unable to start server tokio runtime") - .block_on(async { - let result = GrpcBackendApi::new().await; - - match result { - Ok(backend_api) => { - let backend_api = BackendForCliApiProxy::new(backend_api); - - backend_api.show_settings_window().await.expect("Unknown error") - } - Err(_) => { - tracing::error!("Unable to connect to server. Please check if you have Gauntlet running on your PC") - } - } - }) +pub fn run_app(minimized: bool) { + ui::run(minimized); } diff --git a/rust/client/src/ui/custom_widgets/loading_bar.rs b/rust/client/src/ui/custom_widgets/loading_bar.rs index a682a9e..c2ccb45 100644 --- a/rust/client/src/ui/custom_widgets/loading_bar.rs +++ b/rust/client/src/ui/custom_widgets/loading_bar.rs @@ -19,7 +19,6 @@ use iced::advanced::renderer; use iced::advanced::widget::Tree; use iced::advanced::widget::tree::State; use iced::advanced::widget::tree::Tag; -use iced::event::Status; use iced::mouse::Cursor; use iced::window; @@ -139,6 +138,7 @@ where }, border: Border::default(), shadow: Shadow::default(), + snap: false, }, styling.background_color, ); @@ -156,6 +156,7 @@ where }, border: Border::default(), shadow: Shadow::default(), + snap: false, }, styling.loading_bar_color, ); @@ -172,17 +173,17 @@ where }) } - fn on_event( + fn update( &mut self, state: &mut Tree, - event: Event, + event: &Event, layout: Layout<'_>, _cursor: Cursor, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, _viewport: &Rectangle, - ) -> Status { + ) { const FRAMES_PER_SECOND: u64 = 60; let bounds = layout.bounds(); @@ -190,7 +191,7 @@ where if let Event::Window(window::Event::RedrawRequested(now)) = event { if is_visible(&bounds) { let state = state.state.downcast_mut::(); - let duration = (now - state.last_update).as_secs_f32(); + let duration = (*now - state.last_update).as_secs_f32(); let increment = if self.rate == Duration::ZERO { 0.0 } else { @@ -203,16 +204,14 @@ where state.t -= 1.0; } - shell.request_redraw(window::RedrawRequest::At( - now + Duration::from_millis(1000 / FRAMES_PER_SECOND), + shell.request_redraw_at(window::RedrawRequest::At( + *now + Duration::from_millis(1000 / FRAMES_PER_SECOND), )); - state.last_update = now; + state.last_update = now.clone(); - return Status::Captured; + shell.capture_event(); } } - - Status::Ignored } } @@ -235,7 +234,7 @@ where Renderer: renderer::Renderer + 'a, Theme: 'a + Catalog, { - fn from(spinner: LoadingBar<'a, Theme>) -> Self { - Self::new(spinner) + fn from(loading_bar: LoadingBar<'a, Theme>) -> Self { + Self::new(loading_bar) } } diff --git a/rust/client/src/ui/hud/mod.rs b/rust/client/src/ui/hud/mod.rs index 71bce95..a978ffe 100644 --- a/rust/client/src/ui/hud/mod.rs +++ b/rust/client/src/ui/hud/mod.rs @@ -83,6 +83,7 @@ fn layer_shell_settings() -> iced_layershell::reexport::NewLayerShellSettings { margin: Default::default(), exclusive_zone: Some(0), size: Some((HUD_WINDOW_WIDTH as u32, HUD_WINDOW_HEIGHT as u32)), + namespace: None, } } diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index f35c8ae..91595dc 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -3,9 +3,7 @@ use std::fs; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use std::sync::Mutex; -use anyhow::anyhow; use client_context::ClientContext; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::KeyboardEventOrigin; @@ -29,13 +27,14 @@ use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; use gauntlet_common::scenario_convert::ui_render_location_from_scenario; use gauntlet_common::scenario_model::ScenarioFrontendEvent; use gauntlet_common_ui::physical_key_model; +use gauntlet_server::plugins::ApplicationManager; +use gauntlet_server::plugins::settings::global_shortcut::GlobalShortcutAction; +use gauntlet_server::plugins::settings::global_shortcut::GlobalShortcutPressedEvent; +use gauntlet_server::plugins::settings::global_shortcut::register_listener; use gauntlet_utils::channel::RequestError; -use gauntlet_utils::channel::RequestReceiver; use gauntlet_utils::channel::RequestResult; -use gauntlet_utils::channel::RequestSender; use gauntlet_utils::channel::Responder; -use global_hotkey::GlobalHotKeyManager; -use global_hotkey::hotkey::HotKey; +use gauntlet_utils::channel::channel; use iced::Event; use iced::Length; use iced::Point; @@ -51,6 +50,7 @@ use iced::event; use iced::font; use iced::futures; use iced::futures::SinkExt; +use iced::futures::StreamExt; use iced::keyboard; use iced::keyboard::Key; use iced::keyboard::Modifiers; @@ -100,8 +100,6 @@ mod platform; pub use theme::GauntletComplexTheme; -use crate::global_shortcut::convert_physical_shortcut_to_hotkey; -use crate::global_shortcut::register_listener; use crate::ui::custom_widgets::loading_bar::LoadingBar; use crate::ui::hud::show_hud_window; #[cfg(target_os = "linux")] @@ -121,11 +119,8 @@ use crate::ui::widget::root::render_root; pub struct AppModel { // logic + application_manager: Arc, backend_api: BackendForFrontendApiProxy, - global_hotkey_manager: GlobalHotKeyManager, - current_global_hotkey: Option, - current_entrypoint_global_hotkeys: HashMap<(PluginId, EntrypointId), HotKey>, - frontend_receiver: Arc>>, main_window_id: window::Id, focused: bool, opened: bool, @@ -225,7 +220,7 @@ pub enum AppMsg { ShowWindow, HideWindow, ToggleWindow, - HandleGlobalShortcut(u32), + HandleGlobalShortcut(GlobalShortcutPressedEvent), ToggleActionPanel { keyboard: bool, }, @@ -245,7 +240,6 @@ pub enum AppMsg { ShowPluginErrorView { plugin_id: PluginId, entrypoint_id: EntrypointId, - render_location: UiRenderLocation, }, Screenshot { save_path: String, @@ -293,23 +287,11 @@ pub enum AppMsg { OnAnyActionMainViewSearchResultPanelMouse { widget_id: UiWidgetId, }, - OnPrimaryActionMainViewActionPanelMouse { - widget_id: UiWidgetId, - }, + OnPrimaryActionMainViewActionPanelMouse, ResetMainViewState, OnAnyActionMainViewNoPanelKeyboardAtIndex { index: usize, }, - SetGlobalShortcut { - shortcut: Option, - responder: Arc>>>, - }, - SetGlobalEntrypointShortcut { - plugin_id: PluginId, - entrypoint_id: EntrypointId, - shortcut: Option, - responder: Arc>>>, - }, UpdateLoadingBar { plugin_id: PluginId, entrypoint_id: EntrypointId, @@ -343,9 +325,9 @@ pub enum AppMsg { } #[cfg(target_os = "linux")] -impl TryInto for AppMsg { +impl TryInto for AppMsg { type Error = Self; - fn try_into(self) -> Result { + fn try_into(self) -> Result { match self { Self::LayerShell(msg) => msg.try_into().map_err(|msg| Self::LayerShell(msg)), _ => Err(self), @@ -409,6 +391,7 @@ fn layer_shell_settings() -> iced_layershell::reexport::NewLayerShellSettings { exclusive_zone: Some(0), size: Some((WINDOW_WIDTH as u32, WINDOW_HEIGHT as u32)), use_last_output: false, + namespace: None, } } @@ -436,7 +419,7 @@ fn open_main_window_non_wayland(minimized: bool, window_position_file: Option<&P Task::batch([ open_task.map(|_| AppMsg::Noop), window::gain_focus(main_window_id), - window::change_level(main_window_id, Level::AlwaysOnTop), + window::set_level(main_window_id, Level::AlwaysOnTop), ]), ) } @@ -454,11 +437,7 @@ fn open_main_window_wayland(id: window::Id) -> (window::Id, Task) { ) } -pub fn run( - minimized: bool, - frontend_receiver: RequestReceiver, - backend_sender: RequestSender, -) { +pub fn run(minimized: bool) { #[cfg(target_os = "linux")] let result = { let wayland = std::env::var("WAYLAND_DISPLAY") @@ -466,24 +445,29 @@ pub fn run( .is_ok(); if wayland { - run_wayland(minimized, frontend_receiver, backend_sender) + run_wayland(minimized) } else { - run_non_wayland(minimized, frontend_receiver, backend_sender) + run_non_wayland(minimized) } }; #[cfg(not(target_os = "linux"))] - let result = run_non_wayland(minimized, frontend_receiver, backend_sender); + let result = run_non_wayland(minimized); result.expect("Unable to start application") } -fn run_non_wayland( - minimized: bool, - frontend_receiver: RequestReceiver, - backend_sender: RequestSender, -) -> anyhow::Result<()> { - iced::daemon::(title, update, view) +fn run_non_wayland(minimized: bool) -> anyhow::Result<()> { + let boot = move || { + new( + #[cfg(target_os = "linux")] + false, + minimized, + ) + }; + + iced::daemon::(boot, update, view) + .title(title) .settings(Settings { #[cfg(target_os = "macos")] platform_specific: iced::settings::PlatformSpecific { @@ -494,26 +478,16 @@ fn run_non_wayland( }) .subscription(subscription) .theme(|state, _| state.theme.clone()) - .run_with(move || { - new( - frontend_receiver, - backend_sender, - #[cfg(target_os = "linux")] - false, - minimized, - ) - })?; + .run()?; Ok(()) } #[cfg(target_os = "linux")] -fn run_wayland( - minimized: bool, - frontend_receiver: RequestReceiver, - backend_sender: RequestSender, -) -> anyhow::Result<()> { - iced_layershell::build_pattern::daemon("Gauntlet", update, view, wayland_remove_id_info) +fn run_wayland(minimized: bool) -> anyhow::Result<()> { + let boot = move || new(true, minimized); + + iced_layershell::build_pattern::daemon(boot, "Gauntlet", update, view) .layer_settings(iced_layershell::settings::LayerShellSettings { start_mode: iced_layershell::settings::StartMode::Background, events_transparent: true, @@ -522,55 +496,40 @@ fn run_wayland( ..Default::default() }) .subscription(subscription) - .theme(|state| state.theme.clone()) - .run_with(move || new(frontend_receiver, backend_sender, true, minimized))?; + .theme(|state, _| state.theme.clone()) + .run()?; Ok(()) } -#[cfg(target_os = "linux")] -fn wayland_remove_id_info(_state: &mut AppModel, _id: window::Id) {} +fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, Task) { + let (frontend_sender, frontend_receiver) = channel::(); + let (backend_sender, backend_receiver) = + channel::(); + + let frontend_receiver = Arc::new(TokioRwLock::new(frontend_receiver)); + let application_manager = ApplicationManager::create(frontend_sender).expect("Unable to setup application manager"); + let application_manager = Arc::new(application_manager); + + tokio::spawn({ + let application_manager = application_manager.clone(); + + async move { application_manager.run_grpc_server().await } + }); + + tokio::spawn({ + let application_manager = application_manager.clone(); + + async move { application_manager.run_message_loop(backend_receiver).await } + }); -fn new( - frontend_receiver: RequestReceiver, - backend_sender: RequestSender, - #[cfg(target_os = "linux")] wayland: bool, - minimized: bool, -) -> (AppModel, Task) { let backend_api = BackendForFrontendApiProxy::new(backend_sender); - let setup_data = futures::executor::block_on(backend_api.setup_data()).expect("Unable to setup frontend"); + let setup_data = application_manager.setup().expect("Unable to setup"); let theme = GauntletComplexTheme::new(setup_data.theme); - GauntletComplexTheme::set_global(theme.clone()); - let global_hotkey_manager = GlobalHotKeyManager::new().expect("unable to create global hot key manager"); - - // let current_global_hotkey = None; - // let global_assignment_result = anyhow::Ok(()); - let (current_global_hotkey, global_assignment_result) = - assign_global_shortcut(&global_hotkey_manager, None, setup_data.global_shortcut); - - let mut global_entrypoint_assignment_results = HashMap::new(); - let mut current_entrypoint_global_hotkeys = HashMap::new(); - for ((plugin_id, entrypoint_id), shortcut) in setup_data.global_entrypoint_shortcuts { - let (global_hotkey, result) = assign_global_shortcut(&global_hotkey_manager, None, Some(shortcut)); - if let Some(global_hotkey) = global_hotkey { - current_entrypoint_global_hotkeys.insert((plugin_id.clone(), entrypoint_id.clone()), global_hotkey); - } - global_entrypoint_assignment_results.insert( - (plugin_id, entrypoint_id), - result.map_err(|err| format!("{:#}", err)).err(), - ); - } - - futures::executor::block_on(backend_api.setup_response( - global_assignment_result.map_err(|err| format!("{:#}", err)).err(), - global_entrypoint_assignment_results, - )) - .expect("Unable to setup frontend"); - let mut tasks = vec![font::load(BOOTSTRAP_FONT_BYTES).map(AppMsg::FontLoaded)]; #[cfg(target_os = "linux")] @@ -591,109 +550,22 @@ fn new( tasks.push(open_task); + tasks.push(Task::stream(stream::channel(100, |mut sender| { + async move { + let mut frontend_receiver = frontend_receiver.write().await; + + loop { + let (request_data, responder) = frontend_receiver.recv().await; + + request_loop(request_data, &mut sender, responder).await; + } + } + }))); + let mut client_context = ClientContext::new(); let global_state = if cfg!(feature = "scenario_runner") { - let gen_in = std::env::var("GAUNTLET_SCREENSHOT_GEN_IN").expect("Unable to read GAUNTLET_SCREENSHOT_GEN_IN"); - - println!("Reading scenario file at: {}", gen_in); - - let gen_in = fs::read_to_string(gen_in).expect("Unable to read file at GAUNTLET_SCREENSHOT_GEN_IN"); - - let gen_out = std::env::var("GAUNTLET_SCREENSHOT_GEN_OUT").expect("Unable to read GAUNTLET_SCREENSHOT_GEN_OUT"); - - let gen_name = - std::env::var("GAUNTLET_SCREENSHOT_GEN_NAME").expect("Unable to read GAUNTLET_SCREENSHOT_GEN_NAME"); - - let show_action_panel: bool = std::env::var("GAUNTLET_SCREENSHOT_SHOW_ACTION_PANEL") - .expect("Unable to read GAUNTLET_SCREENSHOT_SHOW_ACTION_PANEL") - .parse() - .expect("Unable to parse GAUNTLET_SCREENSHOT_SHOW_ACTION_PANEL"); - - let event: ScenarioFrontendEvent = - serde_json::from_str(&gen_in).expect("GAUNTLET_SCREENSHOT_GEN_IN is not valid json"); - - tasks.push(Task::perform( - async { - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; - }, - move |_| { - AppMsg::Screenshot { - save_path: gen_out.clone(), - } - }, - )); - - match event { - ScenarioFrontendEvent::ReplaceView { - entrypoint_id, - render_location, - top_level_view, - container, - data: images, - } => { - let plugin_id = PluginId::from_string("__SCREENSHOT_GEN___"); - let entrypoint_id = EntrypointId::from_string(entrypoint_id); - let plugin_name = "Screenshot Plugin".to_string(); - let entrypoint_name = gen_name; - - let render_location = ui_render_location_from_scenario(render_location); - - let _ = client_context.render_ui( - render_location, - Arc::new(container), - images, - &plugin_id, - &plugin_name, - &entrypoint_id, - &entrypoint_name, - ); - - if show_action_panel { - tasks.push(Task::done(AppMsg::ToggleActionPanel { keyboard: false })) - } - - match render_location { - UiRenderLocation::InlineView => GlobalState::new(text_input::Id::unique()), - UiRenderLocation::View => { - GlobalState::new_plugin( - PluginViewData { - top_level_view, - plugin_id, - entrypoint_id, - action_shortcuts: Default::default(), - }, - true, - ) - } - } - } - ScenarioFrontendEvent::ShowPreferenceRequiredView { - entrypoint_id, - plugin_preferences_required, - entrypoint_preferences_required, - } => { - let error_view = ErrorViewData::PreferenceRequired { - plugin_id: PluginId::from_string("__SCREENSHOT_GEN___"), - entrypoint_id: EntrypointId::from_string(entrypoint_id), - plugin_preferences_required, - entrypoint_preferences_required, - }; - - GlobalState::new_error(error_view) - } - ScenarioFrontendEvent::ShowPluginErrorView { - entrypoint_id, - render_location: _, - } => { - let error_view = ErrorViewData::PluginError { - plugin_id: PluginId::from_string("__SCREENSHOT_GEN___"), - entrypoint_id: EntrypointId::from_string(entrypoint_id), - }; - - GlobalState::new_error(error_view) - } - } + scenario_runner_global_state(&mut tasks, &mut client_context) } else { GlobalState::new(text_input::Id::unique()) }; @@ -701,11 +573,8 @@ fn new( ( AppModel { // logic + application_manager, backend_api, - global_hotkey_manager, - current_global_hotkey, - current_entrypoint_global_hotkeys, - frontend_receiver: Arc::new(TokioRwLock::new(frontend_receiver)), main_window_id, focused: false, opened: !minimized, @@ -735,6 +604,108 @@ fn new( ) } +fn scenario_runner_global_state(tasks: &mut Vec>, client_context: &mut ClientContext) -> GlobalState { + let gen_in = std::env::var("GAUNTLET_SCREENSHOT_GEN_IN").expect("Unable to read GAUNTLET_SCREENSHOT_GEN_IN"); + + println!("Reading scenario file at: {}", gen_in); + + let gen_in = fs::read_to_string(gen_in).expect("Unable to read file at GAUNTLET_SCREENSHOT_GEN_IN"); + + let gen_out = std::env::var("GAUNTLET_SCREENSHOT_GEN_OUT").expect("Unable to read GAUNTLET_SCREENSHOT_GEN_OUT"); + + let gen_name = std::env::var("GAUNTLET_SCREENSHOT_GEN_NAME").expect("Unable to read GAUNTLET_SCREENSHOT_GEN_NAME"); + + let show_action_panel: bool = std::env::var("GAUNTLET_SCREENSHOT_SHOW_ACTION_PANEL") + .expect("Unable to read GAUNTLET_SCREENSHOT_SHOW_ACTION_PANEL") + .parse() + .expect("Unable to parse GAUNTLET_SCREENSHOT_SHOW_ACTION_PANEL"); + + let event: ScenarioFrontendEvent = + serde_json::from_str(&gen_in).expect("GAUNTLET_SCREENSHOT_GEN_IN is not valid json"); + + tasks.push(Task::perform( + async { + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + }, + move |_| { + AppMsg::Screenshot { + save_path: gen_out.clone(), + } + }, + )); + + match event { + ScenarioFrontendEvent::ReplaceView { + entrypoint_id, + render_location, + top_level_view, + container, + data: images, + } => { + let plugin_id = PluginId::from_string("__SCREENSHOT_GEN___"); + let entrypoint_id = EntrypointId::from_string(entrypoint_id); + let plugin_name = "Screenshot Plugin".to_string(); + let entrypoint_name = gen_name; + + let render_location = ui_render_location_from_scenario(render_location); + + let _ = client_context.render_ui( + render_location, + Arc::new(container), + images, + &plugin_id, + &plugin_name, + &entrypoint_id, + &entrypoint_name, + ); + + if show_action_panel { + tasks.push(Task::done(AppMsg::ToggleActionPanel { keyboard: false })) + } + + match render_location { + UiRenderLocation::InlineView => GlobalState::new(text_input::Id::unique()), + UiRenderLocation::View => { + GlobalState::new_plugin( + PluginViewData { + top_level_view, + plugin_id, + entrypoint_id, + action_shortcuts: Default::default(), + }, + true, + ) + } + } + } + ScenarioFrontendEvent::ShowPreferenceRequiredView { + entrypoint_id, + plugin_preferences_required, + entrypoint_preferences_required, + } => { + let error_view = ErrorViewData::PreferenceRequired { + plugin_id: PluginId::from_string("__SCREENSHOT_GEN___"), + entrypoint_id: EntrypointId::from_string(entrypoint_id), + plugin_preferences_required, + entrypoint_preferences_required, + }; + + GlobalState::new_error(error_view) + } + ScenarioFrontendEvent::ShowPluginErrorView { + entrypoint_id, + render_location: _, + } => { + let error_view = ErrorViewData::PluginError { + plugin_id: PluginId::from_string("__SCREENSHOT_GEN___"), + entrypoint_id: EntrypointId::from_string(entrypoint_id), + }; + + GlobalState::new_error(error_view) + } + } +} + fn title(state: &AppModel, window: window::Id) -> String { if window == state.main_window_id { "Gauntlet".to_owned() @@ -1184,7 +1155,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { AppMsg::ShowPluginErrorView { plugin_id, entrypoint_id, - .. } => { GlobalState::error( &mut state.global_state, @@ -1364,8 +1334,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { }), ]) } - AppMsg::OnPrimaryActionMainViewActionPanelMouse { widget_id: _ } => { - // widget_id here is always 0 + AppMsg::OnPrimaryActionMainViewActionPanelMouse => { match &state.global_state { GlobalState::MainView { focused_search_result, .. @@ -1467,88 +1436,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { GlobalState::PendingPluginView { .. } => Task::none(), } } - AppMsg::SetGlobalShortcut { shortcut, responder } => { - let responder = responder - .lock() - .expect("lock is poisoned") - .take() - .expect("there should always be a responder here"); - - let (hotkey, error) = assign_global_shortcut( - &state.global_hotkey_manager, - state.current_global_hotkey, - shortcut.clone(), - ); - - state.current_global_hotkey = hotkey; - - match error { - Ok(()) => { - tracing::info!("Successfully registered new global shortcut: {:?}", shortcut); - responder.respond(Ok(FrontendApiResponseData::SetGlobalShortcut { data: () })); - } - Err(err) => { - tracing::error!("Unable to register new global shortcut {:?}: {:?}", shortcut, err); - responder.respond(Err(err)); - } - } - - Task::none() - } - AppMsg::SetGlobalEntrypointShortcut { - plugin_id, - entrypoint_id, - shortcut, - responder, - } => { - let responder = responder - .lock() - .expect("lock is poisoned") - .take() - .expect("there should always be a responder here"); - - let current_global_hotkey = state - .current_entrypoint_global_hotkeys - .get(&(plugin_id.clone(), entrypoint_id.clone())) - .cloned(); - - let (hotkey, error) = - assign_global_shortcut(&state.global_hotkey_manager, current_global_hotkey, shortcut.clone()); - - if let Some(hotkey) = hotkey { - state - .current_entrypoint_global_hotkeys - .insert((plugin_id.clone(), entrypoint_id.clone()), hotkey); - } else { - state - .current_entrypoint_global_hotkeys - .remove(&(plugin_id.clone(), entrypoint_id.clone())); - }; - - match error { - Ok(()) => { - tracing::info!( - "Successfully registered new global shortcut for plugin '{:?}' and entrypoint '{:?}' : {:?}", - plugin_id, - entrypoint_id, - shortcut - ); - responder.respond(Ok(FrontendApiResponseData::SetGlobalEntrypointShortcut { data: () })); - } - Err(err) => { - tracing::info!( - "Unable to register new global shortcut for plugin '{:?}' and entrypoint '{:?}' - {:?}: {:?}", - plugin_id, - entrypoint_id, - shortcut, - err - ); - responder.respond(Err(err)); - } - } - - Task::none() - } AppMsg::UpdateLoadingBar { plugin_id, entrypoint_id, @@ -1670,27 +1557,29 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } } - AppMsg::HandleGlobalShortcut(id) => { - if let Some(hotkey) = state.current_global_hotkey { - if hotkey.id == id { - return Task::done(AppMsg::ToggleWindow); + AppMsg::HandleGlobalShortcut(event) => { + match state.application_manager.handle_global_shortcut_event(event) { + Ok(action) => { + match action { + GlobalShortcutAction::ToggleWindow => Task::done(AppMsg::ToggleWindow), + GlobalShortcutAction::RunEntrypoint { + plugin_id, + entrypoint_id, + } => { + Task::done(AppMsg::RunEntrypoint { + plugin_id, + entrypoint_id, + }) + } + GlobalShortcutAction::Noop => Task::none(), + } + } + Err(err) => { + tracing::error!("Error happened while handling global shortcut: {:?}", err); + + Task::none() } } - - let ids = state - .current_entrypoint_global_hotkeys - .iter() - .find(|(_, hotkey)| hotkey.id == id) - .map(|(ids, _)| ids); - - if let Some((plugin_id, entrypoint_id)) = ids { - return Task::done(AppMsg::RunEntrypoint { - plugin_id: plugin_id.clone(), - entrypoint_id: entrypoint_id.clone(), - }); - }; - - Task::none() } AppMsg::RunEntrypoint { plugin_id, @@ -2148,7 +2037,10 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { None::<&ScrollHandle>, "", || AppMsg::ToggleActionPanel { keyboard: false }, - |widget_id| AppMsg::OnPrimaryActionMainViewActionPanelMouse { widget_id }, + |_widget_id| { + // widget_id here is always 0 + AppMsg::OnPrimaryActionMainViewActionPanelMouse + }, |_widget_id| AppMsg::Noop, || AppMsg::Noop, ) @@ -2167,7 +2059,10 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { Some(focused_action_item), "", || AppMsg::ToggleActionPanel { keyboard: false }, - |widget_id| AppMsg::OnPrimaryActionMainViewActionPanelMouse { widget_id }, + |_widget_id| { + // widget_id here is always 0 + AppMsg::OnPrimaryActionMainViewActionPanelMouse + }, |widget_id| AppMsg::OnAnyActionMainViewSearchResultPanelMouse { widget_id }, || AppMsg::Noop, ) @@ -2186,7 +2081,10 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { Some(focused_action_item), "", || AppMsg::ToggleActionPanel { keyboard: false }, - |widget_id| AppMsg::OnPrimaryActionMainViewActionPanelMouse { widget_id }, + |_widget_id| { + // widget_id here is always 0 + AppMsg::OnPrimaryActionMainViewActionPanelMouse + }, |widget_id| AppMsg::OnAnyActionMainViewInlineViewPanelKeyboardWithFocus { widget_id }, || AppMsg::Noop, ) @@ -2241,13 +2139,6 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { } fn subscription(state: &AppModel) -> Subscription { - let frontend_receiver = state.frontend_receiver.clone(); - - struct RequestLoop; - struct GlobalShortcutListener; - #[cfg(target_os = "linux")] - struct X11ActiveWindowListener; - let events_subscription = event::listen_with(|event, status, window_id| { match status { event::Status::Ignored => Some(AppMsg::IcedEvent(window_id, event)), @@ -2265,51 +2156,32 @@ fn subscription(state: &AppModel) -> Subscription { #[allow(unused_mut)] let mut subscriptions = vec![ - Subscription::run_with_id( - std::any::TypeId::of::(), - stream::channel(10, |sender| { - async move { - register_listener(sender.clone()); + Subscription::run(|| { + stream::channel(10, async move |sender| { + register_listener(sender.clone()); - std::future::pending::<()>().await; + std::future::pending::<()>().await; - unreachable!() - } - }), - ), + unreachable!() + }) + .map(AppMsg::HandleGlobalShortcut) + }), events_subscription, - Subscription::run_with_id( - std::any::TypeId::of::(), - stream::channel(100, |mut sender| { - async move { - let mut frontend_receiver = frontend_receiver.write().await; - - loop { - let (request_data, responder) = frontend_receiver.recv().await; - - request_loop(request_data, &mut sender, responder).await; - } - } - }), - ), ]; #[cfg(target_os = "linux")] if !state.wayland { - let handle = tokio::runtime::Handle::current(); + let subscription = Subscription::run(|| { + stream::channel(100, async move |sender| { + let handle = tokio::runtime::Handle::current(); - let subscription = Subscription::run_with_id( - std::any::TypeId::of::(), - stream::channel(100, |sender| { - async move { - let err = tokio::task::spawn_blocking(|| listen_on_x11_active_window_change(sender, handle)).await; + let err = tokio::task::spawn_blocking(|| listen_on_x11_active_window_change(sender, handle)).await; - if let Err(err) = err { - tracing::error!("error occurred when listening on x11 events: {:?}", err); - } + if let Err(err) = err { + tracing::error!("error occurred when listening on x11 events: {:?}", err); } - }), - ); + }) + }); subscriptions.push(subscription) } @@ -2317,32 +2189,6 @@ fn subscription(state: &AppModel) -> Subscription { Subscription::batch(subscriptions) } -fn assign_global_shortcut( - global_hotkey_manager: &GlobalHotKeyManager, - current_hotkey: Option, - new_shortcut: Option, -) -> (Option, anyhow::Result<()>) { - if let Some(current_hotkey) = current_hotkey { - if let Err(err) = global_hotkey_manager.unregister(current_hotkey.clone()) { - tracing::warn!( - "error occurred when unregistering global shortcut {:?}: {:?}", - current_hotkey, - err - ) - } - } - - if let Some(new_shortcut) = new_shortcut { - let hotkey = convert_physical_shortcut_to_hotkey(new_shortcut); - match global_hotkey_manager.register(hotkey) { - Ok(()) => (Some(hotkey), Ok(())), - Err(err) => (None, Err(anyhow!(err))), - } - } else { - (None, Ok(())) - } -} - impl AppModel { fn on_focused(&mut self) -> Task { self.focused = true; @@ -2385,7 +2231,7 @@ impl AppModel { layer_shell::LayerShellAppMsg::RemoveWindow(self.main_window_id), ))); } else { - commands.push(window::change_mode(self.main_window_id, Mode::Hidden)); + commands.push(window::set_mode(self.main_window_id, Mode::Hidden)); }; #[cfg(not(target_os = "linux"))] @@ -2441,7 +2287,7 @@ impl AppModel { } else { Task::batch([ window::gain_focus(self.main_window_id), - window::change_mode(self.main_window_id, Mode::Windowed), + window::set_mode(self.main_window_id, Mode::Windowed), ]) }; @@ -2754,7 +2600,7 @@ impl AppModel { modifier_alt: false, modifier_meta: cfg!(target_os = "macos"), }) => { - crate::open_settings_window(); + self.application_manager.handle_open_settings_window(); Task::none() } @@ -3027,14 +2873,13 @@ async fn request_loop( FrontendApiRequestData::ShowPluginErrorView { plugin_id, entrypoint_id, - render_location, + render_location: _, } => { responder.respond(Ok(FrontendApiResponseData::ShowPluginErrorView { data: () })); AppMsg::ShowPluginErrorView { plugin_id, entrypoint_id, - render_location, } } FrontendApiRequestData::RequestSearchResultsUpdate {} => { @@ -3047,24 +2892,6 @@ async fn request_loop( AppMsg::ShowHud { display } } - FrontendApiRequestData::SetGlobalShortcut { shortcut } => { - AppMsg::SetGlobalShortcut { - shortcut, - responder: Arc::new(Mutex::new(Some(responder))), - } - } - FrontendApiRequestData::SetGlobalEntrypointShortcut { - plugin_id, - entrypoint_id, - shortcut, - } => { - AppMsg::SetGlobalEntrypointShortcut { - plugin_id, - entrypoint_id, - shortcut, - responder: Arc::new(Mutex::new(Some(responder))), - } - } FrontendApiRequestData::UpdateLoadingBar { plugin_id, entrypoint_id, diff --git a/rust/client/src/ui/sys_tray.rs b/rust/client/src/ui/sys_tray.rs index cd140a5..7b7d8af 100644 --- a/rust/client/src/ui/sys_tray.rs +++ b/rust/client/src/ui/sys_tray.rs @@ -13,8 +13,8 @@ pub fn create_tray() -> tray_icon::TrayIcon { MenuEvent::set_event_handler(Some(|event: MenuEvent| { match event.id().as_ref() { - "GAUNTLET_OPEN_MAIN_WINDOW" => crate::open_window(), - "GAUNTLET_OPEN_SETTING_WINDOW" => crate::open_settings_window(), + "GAUNTLET_OPEN_MAIN_WINDOW" => gauntlet_common::cli::open_window(), + "GAUNTLET_OPEN_SETTING_WINDOW" => gauntlet_common::cli::open_settings_window(), _ => {} } })); diff --git a/rust/client/src/ui/theme/button.rs b/rust/client/src/ui/theme/button.rs index 25501f1..94b2d6e 100644 --- a/rust/client/src/ui/theme/button.rs +++ b/rust/client/src/ui/theme/button.rs @@ -9,17 +9,13 @@ use iced::widget::button::Status; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; -use crate::ui::theme::NOT_INTENDED_TO_BE_USED; use crate::ui::theme::ThemableWidget; use crate::ui::theme::get_theme; use crate::ui::theme::padding_all; #[derive(Debug, Clone, Copy)] pub enum ButtonStyle { - #[allow(unused)] - ShouldNotBeUsed, - - DatePicker, + Default, Action, ActionFocused, @@ -81,8 +77,7 @@ impl ButtonStyle { let theme = &theme.metadata_tag_item_button; theme.padding.to_iced() } - ButtonStyle::ShouldNotBeUsed => padding_all(5.0).to_iced(), - ButtonStyle::DatePicker => padding_all(5.0).to_iced(), + ButtonStyle::Default => padding_all(5.0).to_iced(), } } @@ -253,20 +248,8 @@ impl ButtonStyle { &theme.border_color, ) } - ButtonStyle::ShouldNotBeUsed => { - ( - Some(&NOT_INTENDED_TO_BE_USED), - Some(&NOT_INTENDED_TO_BE_USED), - Some(&NOT_INTENDED_TO_BE_USED), - &NOT_INTENDED_TO_BE_USED, - &NOT_INTENDED_TO_BE_USED, - &0.0, - &1.0, - &Color::TRANSPARENT, - ) - } - ButtonStyle::DatePicker => { - let theme = &theme.form_input_date_picker_buttons; + ButtonStyle::Default => { + let theme = &theme.default_button; ( Some(&theme.background_color), Some(&theme.background_color_hovered), @@ -309,7 +292,8 @@ impl ButtonStyle { } Status::Disabled => { Style { - background: Some(NOT_INTENDED_TO_BE_USED.into()), + // iced currently has a bug where this is shown when the button is not actually disabled + // background: Some(NOT_INTENDED_TO_BE_USED.into()), ..active } } @@ -321,9 +305,7 @@ impl button::Catalog for GauntletComplexTheme { type Class<'a> = ButtonStyle; fn default<'a>() -> Self::Class<'a> { - // TODO Not supposed to be default but unable to customize datepicker buttons right now - // ButtonStyle::ShouldNotBeUsed - ButtonStyle::DatePicker + ButtonStyle::Default } fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { diff --git a/rust/client/src/ui/theme/container.rs b/rust/client/src/ui/theme/container.rs index 34104a1..cbcb5ec 100644 --- a/rust/client/src/ui/theme/container.rs +++ b/rust/client/src/ui/theme/container.rs @@ -118,6 +118,7 @@ impl container::Catalog for GauntletComplexTheme { offset: Vector::new(0.0, 5.0), blur_radius: 25.0, }, + snap: false, } } ContainerStyleInner::ActionShortcutModifier => { @@ -134,6 +135,7 @@ impl container::Catalog for GauntletComplexTheme { color: border_color.clone().into(), }, shadow: Default::default(), + snap: false, } } ContainerStyleInner::ContentCodeBlockText => { @@ -150,6 +152,7 @@ impl container::Catalog for GauntletComplexTheme { color: border_color.clone().into(), }, shadow: Default::default(), + snap: false, } } ContainerStyleInner::Main => { @@ -165,6 +168,7 @@ impl container::Catalog for GauntletComplexTheme { color: theme.border_color, }, shadow: Default::default(), + snap: false, } } ContainerStyleInner::Root => { @@ -180,6 +184,7 @@ impl container::Catalog for GauntletComplexTheme { color: theme.border_color, }, shadow: Default::default(), + snap: false, } } ContainerStyleInner::Tooltip => { @@ -196,6 +201,7 @@ impl container::Catalog for GauntletComplexTheme { color: theme.border_color, }, shadow: Default::default(), + snap: false, } } ContainerStyleInner::ContentImage => { @@ -257,6 +263,7 @@ impl container::Catalog for GauntletComplexTheme { color: theme.border_color, }, shadow: Default::default(), + snap: false, } } ContainerStyleInner::MainListItemAlias => { @@ -271,6 +278,7 @@ impl container::Catalog for GauntletComplexTheme { radius: theme.border_radius.into(), }, shadow: Default::default(), + snap: false, } } } diff --git a/rust/client/src/ui/theme/date_picker.rs b/rust/client/src/ui/theme/date_picker.rs deleted file mode 100644 index b1aad92..0000000 --- a/rust/client/src/ui/theme/date_picker.rs +++ /dev/null @@ -1,84 +0,0 @@ -use iced::Color; -use iced_aw::DatePicker; -use iced_aw::date_picker::Style; -use iced_aw::style::Status; -use iced_aw::style::date_picker::Catalog; - -use crate::ui::theme::Element; -use crate::ui::theme::GauntletComplexTheme; -use crate::ui::theme::ThemableWidget; - -#[derive(Clone, Default)] -pub enum DatePickerStyle { - #[default] - Default, -} - -impl Catalog for GauntletComplexTheme { - type Class<'a> = DatePickerStyle; - - fn default<'a>() -> Self::Class<'a> { - DatePickerStyle::Default - } - - fn style(&self, _class: &Self::Class<'_>, status: Status) -> Style { - match status { - Status::Active => active(self), - Status::Hovered => hovered(self), - Status::Pressed => hovered(self), // TODO proper styling - Status::Disabled => hovered(self), // TODO proper styling - Status::Focused => focused(self), - Status::Selected => selected(self), - } - } -} - -fn active(theme: &GauntletComplexTheme) -> Style { - let root_theme = &theme.popup; - let theme = &theme.form_input_date_picker; - - Style { - background: theme.background_color.into(), - border_radius: root_theme.border_radius, - border_width: root_theme.border_width, - border_color: root_theme.border_color, - text_color: theme.text_color, - text_attenuated_color: theme.text_attenuated_color, - day_background: theme.day_background_color.into(), - } -} - -fn selected(theme: &GauntletComplexTheme) -> Style { - let form_theme = &theme.form_input_date_picker; - - Style { - day_background: form_theme.day_background_color_selected.into(), - text_color: form_theme.text_color_selected, - ..active(theme) - } -} - -fn hovered(theme: &GauntletComplexTheme) -> Style { - let form_theme = &theme.form_input_date_picker; - - Style { - day_background: form_theme.day_background_color_hovered.into(), - text_color: form_theme.text_color_hovered, - ..active(theme) - } -} - -fn focused(theme: &GauntletComplexTheme) -> Style { - Style { - border_color: Color::from_rgb(0.5, 0.5, 0.5), // TODO move to theme? - ..active(theme) - } -} - -impl<'a, Message: 'a + Clone + 'static> ThemableWidget<'a, Message> for DatePicker<'a, Message, GauntletComplexTheme> { - type Kind = DatePickerStyle; - - fn themed(self, kind: DatePickerStyle) -> Element<'a, Message> { - self.class(kind).into() - } -} diff --git a/rust/client/src/ui/theme/grid.rs b/rust/client/src/ui/theme/grid.rs index b8e6961..8907d82 100644 --- a/rust/client/src/ui/theme/grid.rs +++ b/rust/client/src/ui/theme/grid.rs @@ -1,5 +1,5 @@ use iced::Renderer; -use iced_aw::Grid; +use iced::widget::Grid; use crate::ui::theme::Element; use crate::ui::theme::GauntletComplexTheme; diff --git a/rust/client/src/ui/theme/mod.rs b/rust/client/src/ui/theme/mod.rs index 7584e34..eb566d3 100644 --- a/rust/client/src/ui/theme/mod.rs +++ b/rust/client/src/ui/theme/mod.rs @@ -7,13 +7,13 @@ use gauntlet_common::model::UiThemeColor; use gauntlet_common::model::UiThemeMode; use iced::Color; use iced::Padding; -use iced::application; -use iced::application::DefaultStyle; +use iced::theme::Base; +use iced::theme::Palette; +use iced::theme::Style; pub mod button; pub mod checkbox; pub mod container; -pub mod date_picker; pub mod grid; pub mod image; mod loading_bar; @@ -35,6 +35,7 @@ pub struct GauntletComplexTheme { root: ThemeRoot, popup: ThemeRoot, action: ThemeButton, + default_button: ThemeButton, action_panel: ThemePaddingBackgroundColor, action_panel_title: ThemePaddingTextColor, action_section_title: ThemePaddingTextColor, @@ -53,8 +54,6 @@ pub struct GauntletComplexTheme { form_inner: ThemePaddingOnly, form_input: ThemePaddingOnly, form_input_label: ThemePaddingOnly, - form_input_date_picker: ThemeDatePicker, - form_input_date_picker_buttons: ThemeButton, form_input_checkbox: ThemeCheckbox, form_input_select: ThemeSelect, form_input_select_menu: ThemeSelectMenu, @@ -184,6 +183,17 @@ impl GauntletComplexTheme { border_width: window.border.width, border_color: to_iced(&window.border.color), }, + default_button: ThemeButton { + padding: padding_all(8.0), + background_color: background_200, + background_color_focused: background_100, + background_color_hovered: background_100, + text_color: text_100, + text_color_hovered: text_100, + border_radius: content.border.radius, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, action_panel: ThemePaddingBackgroundColor { padding: padding_all(8.0), background_color: background_400, @@ -489,27 +499,6 @@ impl GauntletComplexTheme { text_color_hovered: text_200, }, empty_view_subtitle: ThemeTextColor { text_color: text_300 }, - form_input_date_picker: ThemeDatePicker { - background_color: background_400, - text_color: text_100, - text_color_selected: text_300, - text_color_hovered: text_300, - text_attenuated_color: text_300, - day_background_color: background_200, - day_background_color_selected: background_200, - day_background_color_hovered: background_200, - }, - form_input_date_picker_buttons: ThemeButton { - padding: padding_all(8.0), - background_color: background_200, - background_color_focused: background_100, - background_color_hovered: background_100, - text_color: text_100, - text_color_hovered: text_100, - border_radius: content.border.radius, - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, form_input_checkbox: ThemeCheckbox { background_color_checked: text_200, background_color_unchecked: Color::TRANSPARENT, @@ -753,21 +742,6 @@ pub struct ThemeInline { border_color: Color, } -#[derive(Debug, Clone)] -pub struct ThemeDatePicker { - background_color: Color, - - text_color: Color, - text_color_selected: Color, - text_color_hovered: Color, - - text_attenuated_color: Color, - - day_background_color: Color, - day_background_color_selected: Color, - day_background_color_hovered: Color, -} - #[derive(Debug, Clone)] pub struct ThemePaddingTextColor { padding: ThemePadding, @@ -920,25 +894,17 @@ pub trait ThemableWidget<'a, Message> { fn themed(self, name: Self::Kind) -> Element<'a, Message>; } -impl DefaultStyle for GauntletComplexTheme { - fn default_style(&self) -> application::Appearance { +impl Base for GauntletComplexTheme { + fn base(&self) -> Style { let theme = get_theme(); - application::Appearance { + Style { background_color: Color::TRANSPARENT, text_color: theme.text, } } -} -#[cfg(target_os = "linux")] -impl iced_layershell::DefaultStyle for GauntletComplexTheme { - fn default_style(&self) -> iced_layershell::Appearance { - let theme = get_theme(); - - iced_layershell::Appearance { - background_color: Color::TRANSPARENT, - text_color: theme.text, - } + fn palette(&self) -> Option { + Some(Palette::FERRA) } } diff --git a/rust/client/src/ui/theme/pick_list.rs b/rust/client/src/ui/theme/pick_list.rs index 6262210..33673cf 100644 --- a/rust/client/src/ui/theme/pick_list.rs +++ b/rust/client/src/ui/theme/pick_list.rs @@ -36,12 +36,12 @@ impl pick_list::Catalog for GauntletComplexTheme { let theme = &theme.form_input_select; let background_color = match status { - Status::Active | Status::Opened => theme.background_color, + Status::Active | Status::Opened { is_hovered: _ } => theme.background_color, Status::Hovered => theme.background_color_hovered, }; let text_color = match status { - Status::Active | Status::Opened => theme.text_color, + Status::Active | Status::Opened { is_hovered: _ } => theme.text_color, Status::Hovered => theme.text_color_hovered, }; diff --git a/rust/client/src/ui/theme/rule.rs b/rust/client/src/ui/theme/rule.rs index 9031970..c359272 100644 --- a/rust/client/src/ui/theme/rule.rs +++ b/rust/client/src/ui/theme/rule.rs @@ -27,6 +27,7 @@ impl rule::Catalog for GauntletComplexTheme { width: 1, radius: 0.0.into(), fill_mode: rule::FillMode::Full, + snap: false, } } RuleStyle::ActionPanel => { @@ -35,6 +36,7 @@ impl rule::Catalog for GauntletComplexTheme { width: 1, radius: 0.0.into(), fill_mode: rule::FillMode::Percent(96.0), + snap: false, } } RuleStyle::PrimaryActionSeparator => { @@ -43,6 +45,7 @@ impl rule::Catalog for GauntletComplexTheme { width: 1, radius: 0.0.into(), fill_mode: rule::FillMode::Percent(70.0), + snap: false, } } } diff --git a/rust/client/src/ui/theme/scrollable.rs b/rust/client/src/ui/theme/scrollable.rs index da755bb..e057de3 100644 --- a/rust/client/src/ui/theme/scrollable.rs +++ b/rust/client/src/ui/theme/scrollable.rs @@ -33,7 +33,7 @@ impl scrollable::Catalog for GauntletComplexTheme { }; match status { - Status::Active => { + Status::Active { .. } => { Style { container: container::Style::default(), vertical_rail: scrollbar, @@ -44,6 +44,7 @@ impl scrollable::Catalog for GauntletComplexTheme { Status::Hovered { is_horizontal_scrollbar_hovered, is_vertical_scrollbar_hovered, + .. } => { let hovered_scrollbar = scrollable::Rail { scroller: scrollable::Scroller { @@ -71,6 +72,7 @@ impl scrollable::Catalog for GauntletComplexTheme { Status::Dragged { is_horizontal_scrollbar_dragged, is_vertical_scrollbar_dragged, + .. } => { let dragged_scrollbar = scrollable::Rail { scroller: scrollable::Scroller { diff --git a/rust/client/src/ui/theme/text_input.rs b/rust/client/src/ui/theme/text_input.rs index 1fcd8b8..900d3e4 100644 --- a/rust/client/src/ui/theme/text_input.rs +++ b/rust/client/src/ui/theme/text_input.rs @@ -30,7 +30,7 @@ impl text_input::Catalog for GauntletComplexTheme { match status { Status::Active => active(self, class), Status::Hovered => focused(self, class), // TODO proper style - Status::Focused => focused(self, class), + Status::Focused { is_hovered: _ } => focused(self, class), // TODO proper style Status::Disabled => disabled(), } } diff --git a/rust/client/src/ui/widget/data.rs b/rust/client/src/ui/widget/data.rs index 3d2e247..cec4f2c 100644 --- a/rust/client/src/ui/widget/data.rs +++ b/rust/client/src/ui/widget/data.rs @@ -27,7 +27,6 @@ use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::grid::grid_width; use crate::ui::widget::state::CheckboxState; use crate::ui::widget::state::ComponentWidgetState; -use crate::ui::widget::state::DatePickerState; use crate::ui::widget::state::RootState; use crate::ui::widget::state::SelectState; use crate::ui::widget::state::TextFieldState; @@ -76,18 +75,6 @@ impl<'b> ComponentWidgets<'b> { } } - pub fn date_picker_state(&self, widget_id: UiWidgetId) -> &DatePickerState { - let state = self.state.get(&widget_id).expect(&format!( - "requested state should always be present for id: {}", - widget_id - )); - - match state { - ComponentWidgetState::DatePicker(state) => state, - _ => panic!("TextFieldState expected, {:?} found", state), - } - } - pub fn select_state(&self, widget_id: UiWidgetId) -> &SelectState { let state = self.state.get(&widget_id).expect(&format!( "requested state should always be present for id: {}", diff --git a/rust/client/src/ui/widget/events.rs b/rust/client/src/ui/widget/events.rs index 2800130..071194e 100644 --- a/rust/client/src/ui/widget/events.rs +++ b/rust/client/src/ui/widget/events.rs @@ -5,7 +5,6 @@ use crate::model::UiViewEvent; use crate::ui::AppMsg; use crate::ui::widget::state::CheckboxState; use crate::ui::widget::state::ComponentWidgetState; -use crate::ui::widget::state::DatePickerState; use crate::ui::widget::state::SelectState; use crate::ui::widget::state::TextFieldState; @@ -28,9 +27,6 @@ pub enum ComponentWidgetEvent { widget_id: UiWidgetId, id: Option, }, - ToggleDatePicker { - widget_id: UiWidgetId, - }, OnChangeTextField { widget_id: UiWidgetId, value: String, @@ -43,13 +39,6 @@ pub enum ComponentWidgetEvent { widget_id: UiWidgetId, value: String, }, - SubmitDatePicker { - widget_id: UiWidgetId, - value: String, - }, - CancelDatePicker { - widget_id: UiWidgetId, - }, ToggleCheckbox { widget_id: UiWidgetId, value: bool, @@ -85,45 +74,6 @@ impl ComponentWidgetEvent { ComponentWidgetEvent::RunAction { widget_id, id } | ComponentWidgetEvent::ActionClick { widget_id, id } => { Some(create_action_on_action_event(widget_id, id)) } - ComponentWidgetEvent::ToggleDatePicker { widget_id } => { - let Some(state) = state else { - return None; - }; - - let ComponentWidgetState::DatePicker(DatePickerState { show_picker, .. }) = state else { - panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) - }; - - *show_picker = !*show_picker; - - None - } - ComponentWidgetEvent::CancelDatePicker { widget_id } => { - let Some(state) = state else { - return None; - }; - - let ComponentWidgetState::DatePicker(DatePickerState { show_picker, .. }) = state else { - panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) - }; - - *show_picker = false; - - None - } - ComponentWidgetEvent::SubmitDatePicker { widget_id, value } => { - let Some(state) = state else { - return None; - }; - - let ComponentWidgetState::DatePicker(DatePickerState { show_picker, .. }) = state else { - panic!("unexpected state kind, widget_id: {:?} state: {:?}", widget_id, state) - }; - - *show_picker = false; - - Some(create_date_picker_on_change_event(widget_id, Some(value))) - } ComponentWidgetEvent::ToggleCheckbox { widget_id, value } => { let Some(state) = state else { return None; @@ -219,9 +169,6 @@ impl ComponentWidgetEvent { ComponentWidgetEvent::ActionClick { widget_id, .. } => widget_id, ComponentWidgetEvent::RunAction { widget_id, .. } => widget_id, ComponentWidgetEvent::TagClick { widget_id, .. } => widget_id, - ComponentWidgetEvent::ToggleDatePicker { widget_id, .. } => widget_id, - ComponentWidgetEvent::SubmitDatePicker { widget_id, .. } => widget_id, - ComponentWidgetEvent::CancelDatePicker { widget_id, .. } => widget_id, ComponentWidgetEvent::ToggleCheckbox { widget_id, .. } => widget_id, ComponentWidgetEvent::SelectPickList { widget_id, .. } => widget_id, ComponentWidgetEvent::OnChangeTextField { widget_id, .. } => widget_id, diff --git a/rust/client/src/ui/widget/form.rs b/rust/client/src/ui/widget/form.rs index a661f6e..3b934db 100644 --- a/rust/client/src/ui/widget/form.rs +++ b/rust/client/src/ui/widget/form.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::fmt::Display; use gauntlet_common::model::CheckboxWidget; -use gauntlet_common::model::DatePickerWidget; use gauntlet_common::model::FormWidget; use gauntlet_common::model::FormWidgetOrderedMembers; use gauntlet_common::model::PasswordFieldWidget; @@ -16,7 +15,6 @@ use iced::Length; use iced::advanced::text::Shaping; use iced::alignment::Horizontal; use iced::widget::Space; -use iced::widget::button; use iced::widget::checkbox; use iced::widget::column; use iced::widget::container; @@ -26,20 +24,17 @@ use iced::widget::row; use iced::widget::scrollable; use iced::widget::text; use iced::widget::text_input; -use iced_aw::date_picker; use crate::ui::state::PluginViewState; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; use crate::ui::theme::container::ContainerStyle; -use crate::ui::theme::date_picker::DatePickerStyle; use crate::ui::theme::pick_list::PickListStyle; use crate::ui::theme::row::RowStyle; use crate::ui::theme::text_input::TextInputStyle; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::state::CheckboxState; -use crate::ui::widget::state::DatePickerState; use crate::ui::widget::state::RootState; use crate::ui::widget::state::SelectState; use crate::ui::widget::state::TextFieldState; @@ -73,38 +68,6 @@ impl<'b> ComponentWidgets<'b> { .into() } - fn render_date_picker_widget<'a>(&self, widget: &DatePickerWidget) -> Element<'a, ComponentWidgetEvent> { - let widget_id = widget.__id__; - let DatePickerState { - state_value, - show_picker, - } = self.date_picker_state(widget.__id__); - - let button_text = text(state_value.to_string()).shaping(Shaping::Advanced); - - let button = button(button_text).on_press(ComponentWidgetEvent::ToggleDatePicker { - widget_id: widget.__id__, - }); - - // TODO unable to customize buttons here, split to separate button styles - // DatePickerUnderlay, - // DatePickerOverlay, - - date_picker( - show_picker.to_owned(), - state_value.to_owned(), - button, - ComponentWidgetEvent::CancelDatePicker { widget_id }, - move |date| { - ComponentWidgetEvent::SubmitDatePicker { - widget_id, - value: date.to_string(), - } - }, - ) - .themed(DatePickerStyle::Default) - } - fn render_select_widget<'a>(&self, widget: &SelectWidget) -> Element<'a, ComponentWidgetEvent> { let widget_id = widget.__id__; let SelectState { state_value } = self.select_state(widget_id); @@ -200,9 +163,6 @@ impl<'b> ComponentWidgets<'b> { FormWidgetOrderedMembers::Checkbox(widget) => { render_field(self.render_checkbox_widget(widget), &widget.label) } - FormWidgetOrderedMembers::DatePicker(widget) => { - render_field(self.render_date_picker_widget(widget), &widget.label) - } FormWidgetOrderedMembers::Select(widget) => { render_field(self.render_select_widget(widget), &widget.label) } diff --git a/rust/client/src/ui/widget/grid.rs b/rust/client/src/ui/widget/grid.rs index bb458ab..1ac3361 100644 --- a/rust/client/src/ui/widget/grid.rs +++ b/rust/client/src/ui/widget/grid.rs @@ -9,17 +9,14 @@ use gauntlet_common::model::GridWidgetOrderedMembers; use gauntlet_common::model::PhysicalShortcut; use iced::Length; use iced::advanced::text::Shaping; -use iced::alignment::Vertical; use iced::widget::button; use iced::widget::column; use iced::widget::container; +use iced::widget::grid; use iced::widget::horizontal_space; use iced::widget::row; use iced::widget::scrollable; use iced::widget::text; -use iced_aw::GridRow; -use iced_aw::grid; -use iced_aw::grid_row; use itertools::Itertools; use crate::ui::state::PluginViewState; @@ -264,25 +261,17 @@ impl<'b> ComponentWidgets<'b> { // Some(value) => panic!("unsupported aspect_ratio {:?}", value) // }; - let grid_width = grid_width(columns); + let columns = grid_width(columns); - let rows: Vec> = items + let rows: Vec> = items .iter() - .map(|widget| self.render_grid_item_widget(widget, item_focus_index, index_counter, grid_width)) - .chunks(grid_width) + .map(|widget| self.render_grid_item_widget(widget, item_focus_index, index_counter, columns)) + .chunks(columns) .into_iter() - .map(|row_items| { - let mut row_items: Vec<_> = row_items.collect(); - row_items.resize_with(grid_width, || horizontal_space().into()); - - grid_row(row_items).into() - }) + .flat_map(|row_items| row_items) .collect(); - let grid: Element<_> = grid(rows) - .width(Length::Fill) - .vertical_alignment(Vertical::Top) - .themed(GridStyle::Default); + let grid = grid(rows).columns(columns).themed(GridStyle::Default); grid } diff --git a/rust/client/src/ui/widget/images.rs b/rust/client/src/ui/widget/images.rs index 7571126..3e660ed 100644 --- a/rust/client/src/ui/widget/images.rs +++ b/rust/client/src/ui/widget/images.rs @@ -3,13 +3,12 @@ use std::collections::HashMap; use gauntlet_common::model::Icons; use gauntlet_common::model::ImageLike; use gauntlet_common::model::UiWidgetId; +use iced::widget::Text; use iced::widget::horizontal_space; use iced::widget::image; use iced::widget::svg; -use iced::widget::value; -use iced_fonts::BOOTSTRAP_FONT; -use iced_fonts::Bootstrap; +use crate::ui::GauntletComplexTheme; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; use crate::ui::theme::text::TextStyle; @@ -29,8 +28,8 @@ pub fn render_image<'a, T: 'a + Clone>( } ImageLike::Icons(icon) => { match icon_style { - None => value(icon_to_bootstrap(icon)).font(BOOTSTRAP_FONT).into(), - Some(icon_style) => value(icon_to_bootstrap(icon)).font(BOOTSTRAP_FONT).themed(icon_style), + None => icon_to_bootstrap(icon).into(), + Some(icon_style) => icon_to_bootstrap(icon).themed(icon_style), } } } @@ -43,227 +42,228 @@ pub fn render_svg<'a, T: 'a>(data: &HashMap>, widget_id: UiW } } -pub fn icon_to_bootstrap(icon: &Icons) -> Bootstrap { +pub fn icon_to_bootstrap<'a>(icon: &Icons) -> Text<'a, GauntletComplexTheme> { + use iced_fonts::bootstrap::*; match icon { - Icons::Airplane => Bootstrap::Airplane, - Icons::Alarm => Bootstrap::Alarm, - Icons::AlignCentre => Bootstrap::AlignCenter, - Icons::AlignLeft => Bootstrap::AlignStart, - Icons::AlignRight => Bootstrap::AlignEnd, - // Icons::Anchor => Bootstrap::, - Icons::ArrowClockwise => Bootstrap::ArrowClockwise, - Icons::ArrowCounterClockwise => Bootstrap::ArrowCounterclockwise, - Icons::ArrowDown => Bootstrap::ArrowDown, - Icons::ArrowLeft => Bootstrap::ArrowLeft, - Icons::ArrowRight => Bootstrap::ArrowRight, - Icons::ArrowUp => Bootstrap::ArrowUp, - Icons::ArrowLeftRight => Bootstrap::ArrowLeftRight, - Icons::ArrowsContract => Bootstrap::ArrowsAngleContract, - Icons::ArrowsExpand => Bootstrap::ArrowsAngleExpand, - Icons::AtSymbol => Bootstrap::At, - // Icons::BandAid => Bootstrap::Bandaid, - Icons::Cash => Bootstrap::Cash, - // Icons::BarChart => Bootstrap::BarChart, - // Icons::BarCode => Bootstrap::, - Icons::Battery => Bootstrap::Battery, - Icons::BatteryCharging => Bootstrap::BatteryCharging, - // Icons::BatteryDisabled => Bootstrap::, - Icons::Bell => Bootstrap::Bell, - Icons::BellDisabled => Bootstrap::BellSlash, - // Icons::Bike => Bootstrap::Bicycle, - // Icons::Binoculars => Bootstrap::Binoculars, - // Icons::Bird => Bootstrap::, - Icons::Bluetooth => Bootstrap::Bluetooth, - // Icons::Boat => Bootstrap::, - Icons::Bold => Bootstrap::TypeBold, - // Icons::Bolt => Bootstrap::, - // Icons::BoltDisabled => Bootstrap::, - Icons::Book => Bootstrap::Book, - Icons::Bookmark => Bootstrap::Bookmark, - Icons::Box => Bootstrap::Box, - // Icons::Brush => Bootstrap::Brush, - Icons::Bug => Bootstrap::Bug, - Icons::Building => Bootstrap::Building, - Icons::BulletPoints => Bootstrap::ListUl, - Icons::Calculator => Bootstrap::Calculator, - Icons::Calendar => Bootstrap::Calendar, - Icons::Camera => Bootstrap::Camera, - Icons::Car => Bootstrap::CarFront, - Icons::Cart => Bootstrap::Cart, - // Icons::Cd => Bootstrap::, - // Icons::Center => Bootstrap::, - Icons::Checkmark => Bootstrap::Checktwo, - // Icons::ChessPiece => Bootstrap::, - Icons::ChevronDown => Bootstrap::ChevronDown, - Icons::ChevronLeft => Bootstrap::ChevronLeft, - Icons::ChevronRight => Bootstrap::ChevronRight, - Icons::ChevronUp => Bootstrap::ChevronUp, - Icons::ChevronExpand => Bootstrap::ChevronExpand, - Icons::Circle => Bootstrap::Circle, - // Icons::CircleProgress100 => Bootstrap::, - // Icons::CircleProgress25 => Bootstrap::, - // Icons::CircleProgress50 => Bootstrap::, - // Icons::CircleProgress75 => Bootstrap::, - // Icons::ClearFormatting => Bootstrap::, - Icons::Clipboard => Bootstrap::Clipboard, - Icons::Clock => Bootstrap::Clock, - Icons::Cloud => Bootstrap::Cloud, - Icons::CloudLightning => Bootstrap::CloudLightning, - Icons::CloudRain => Bootstrap::CloudRain, - Icons::CloudSnow => Bootstrap::CloudSnow, - Icons::CloudSun => Bootstrap::CloudSun, - Icons::Code => Bootstrap::Code, - Icons::Gear => Bootstrap::Gear, - Icons::Coin => Bootstrap::Coin, - Icons::Command => Bootstrap::Command, - Icons::Compass => Bootstrap::Compass, - // Icons::ComputerChip => Bootstrap::, - // Icons::Contrast => Bootstrap::, - Icons::CreditCard => Bootstrap::CreditCard, - Icons::Crop => Bootstrap::Crop, - // Icons::Crown => Bootstrap::, - Icons::Document => Bootstrap::FileEarmark, - Icons::DocumentAdd => Bootstrap::FileEarmarkPlus, - Icons::DocumentDelete => Bootstrap::FileEarmarkX, - Icons::Dot => Bootstrap::Dot, - Icons::Download => Bootstrap::Download, - // Icons::Duplicate => Bootstrap::, - Icons::Eject => Bootstrap::Eject, - Icons::ThreeDots => Bootstrap::ThreeDots, - Icons::Envelope => Bootstrap::Envelope, - Icons::Eraser => Bootstrap::Eraser, - Icons::ExclamationMark => Bootstrap::ExclamationLg, - Icons::Eye => Bootstrap::Eye, - Icons::EyeDisabled => Bootstrap::EyeSlash, - Icons::EyeDropper => Bootstrap::Eyedropper, - Icons::Female => Bootstrap::GenderFemale, - Icons::Film => Bootstrap::Film, - Icons::Filter => Bootstrap::Filter, - Icons::Fingerprint => Bootstrap::Fingerprint, - Icons::Flag => Bootstrap::Flag, - Icons::Folder => Bootstrap::Folder, - Icons::FolderAdd => Bootstrap::FolderPlus, - Icons::FolderDelete => Bootstrap::FolderMinus, - Icons::Forward => Bootstrap::Forward, - Icons::GameController => Bootstrap::Controller, - Icons::Virus => Bootstrap::Virus, - Icons::Gift => Bootstrap::Gift, - Icons::Glasses => Bootstrap::Eyeglasses, - Icons::Globe => Bootstrap::Globe, - Icons::Hammer => Bootstrap::Hammer, - Icons::HardDrive => Bootstrap::DeviceHdd, - Icons::Headphones => Bootstrap::Headphones, - Icons::Heart => Bootstrap::Heart, - // Icons::HeartDisabled => Bootstrap::, - Icons::Heartbeat => Bootstrap::Activity, - Icons::Hourglass => Bootstrap::Hourglass, - Icons::House => Bootstrap::House, - Icons::Image => Bootstrap::Image, - Icons::Info => Bootstrap::InfoLg, - Icons::Italics => Bootstrap::TypeItalic, - Icons::Key => Bootstrap::Key, - Icons::Keyboard => Bootstrap::Keyboard, - Icons::Layers => Bootstrap::Layers, - // Icons::Leaf => Bootstrap::, - Icons::LightBulb => Bootstrap::Lightbulb, - Icons::LightBulbDisabled => Bootstrap::LightbulbOff, - Icons::Link => Bootstrap::LinkFourfivedeg, - Icons::List => Bootstrap::List, - Icons::Lock => Bootstrap::Lock, - // Icons::LockDisabled => Bootstrap::, - Icons::LockUnlocked => Bootstrap::Unlock, - // Icons::Logout => Bootstrap::, - // Icons::Lowercase => Bootstrap::, - // Icons::MagnifyingGlass => Bootstrap::, - Icons::Male => Bootstrap::GenderMale, - Icons::Map => Bootstrap::Map, - Icons::Maximize => Bootstrap::Fullscreen, - Icons::Megaphone => Bootstrap::Megaphone, - Icons::MemoryModule => Bootstrap::Memory, - Icons::MemoryStick => Bootstrap::UsbDrive, - Icons::Message => Bootstrap::Chat, - Icons::Microphone => Bootstrap::Mic, - Icons::MicrophoneDisabled => Bootstrap::MicMute, - Icons::Minimize => Bootstrap::FullscreenExit, - Icons::Minus => Bootstrap::Dash, - Icons::Mobile => Bootstrap::Phone, - // Icons::Monitor => Bootstrap::, - Icons::Moon => Bootstrap::Moon, - // Icons::Mountain => Bootstrap::, - Icons::Mouse => Bootstrap::Mouse, - Icons::Multiply => Bootstrap::X, - Icons::Music => Bootstrap::MusicNoteBeamed, - Icons::Network => Bootstrap::BroadcastPin, - Icons::Paperclip => Bootstrap::Paperclip, - Icons::Paragraph => Bootstrap::TextParagraph, - Icons::Pause => Bootstrap::Pause, - Icons::Pencil => Bootstrap::Pencil, - Icons::Person => Bootstrap::Person, - Icons::PersonAdd => Bootstrap::PersonAdd, - Icons::PersonRemove => Bootstrap::PersonDash, - Icons::Phone => Bootstrap::Telephone, - // Icons::PhoneRinging => Bootstrap::, - Icons::PieChart => Bootstrap::PieChart, - Icons::Capsule => Bootstrap::Capsule, - // Icons::Pin => Bootstrap::, - // Icons::PinDisabled => Bootstrap::, - Icons::Play => Bootstrap::Play, - Icons::Plug => Bootstrap::Plug, - Icons::Plus => Bootstrap::Plus, - // Icons::PlusMinusDivideMultiply => Bootstrap::, - Icons::Power => Bootstrap::Power, - Icons::Printer => Bootstrap::Printer, - Icons::QuestionMark => Bootstrap::QuestionLg, - Icons::Quotes => Bootstrap::Quote, - Icons::Receipt => Bootstrap::Receipt, - Icons::Repeat => Bootstrap::Repeat, - Icons::Reply => Bootstrap::Reply, - Icons::Rewind => Bootstrap::Rewind, - Icons::Rocket => Bootstrap::Rocket, - // Icons::Ruler => Bootstrap::, - Icons::Shield => Bootstrap::Shield, - Icons::Shuffle => Bootstrap::Shuffle, - Icons::Snippets => Bootstrap::BodyText, - Icons::Snowflake => Bootstrap::Snow, - // Icons::VolumeHigh => Bootstrap::VolumeUp, - // Icons::VolumeLow => Bootstrap::VolumeDown, - // Icons::VolumeOff => Bootstrap::VolumeOff, - // Icons::VolumeOn => Bootstrap::, - Icons::Star => Bootstrap::Star, - // Icons::StarDisabled => Bootstrap::, - Icons::Stop => Bootstrap::Stop, - Icons::Stopwatch => Bootstrap::Stopwatch, - Icons::StrikeThrough => Bootstrap::TypeStrikethrough, - Icons::Sun => Bootstrap::SunFill, // TODO why Sun isn't in iced_aw? - Icons::Scissors => Bootstrap::Scissors, - // Icons::Syringe => Bootstrap::, - Icons::Tag => Bootstrap::Tag, - Icons::Thermometer => Bootstrap::Thermometer, - Icons::Terminal => Bootstrap::Terminal, - Icons::Text => Bootstrap::Fonts, - Icons::TextCursor => Bootstrap::CursorText, - // Icons::TextSelection => Bootstrap::, - // Icons::Torch => Bootstrap::, - // Icons::Train => Bootstrap::, - Icons::Trash => Bootstrap::Trash, - Icons::Tree => Bootstrap::Tree, - Icons::Trophy => Bootstrap::Trophy, - Icons::People => Bootstrap::People, - Icons::Umbrella => Bootstrap::Umbrella, - Icons::Underline => Bootstrap::TypeUnderline, - Icons::Upload => Bootstrap::Upload, - // Icons::Uppercase => Bootstrap::, - Icons::Wallet => Bootstrap::Wallet, - Icons::Wand => Bootstrap::Magic, - // Icons::Warning => Bootstrap::, - // Icons::Weights => Bootstrap::, - Icons::Wifi => Bootstrap::Wifi, - Icons::WifiDisabled => Bootstrap::WifiOff, - Icons::Window => Bootstrap::Window, - Icons::Tools => Bootstrap::Tools, - Icons::Watch => Bootstrap::Watch, - Icons::XMark => Bootstrap::XLg, - Icons::Indent => Bootstrap::Indent, - Icons::Unindent => Bootstrap::Unindent, + Icons::Airplane => airplane(), + Icons::Alarm => alarm(), + Icons::AlignCentre => align_center(), + Icons::AlignLeft => align_start(), + Icons::AlignRight => align_end(), + // Icons::Anchor => , + Icons::ArrowClockwise => arrow_clockwise(), + Icons::ArrowCounterClockwise => arrow_counterclockwise(), + Icons::ArrowDown => arrow_down(), + Icons::ArrowLeft => arrow_left(), + Icons::ArrowRight => arrow_right(), + Icons::ArrowUp => arrow_up(), + Icons::ArrowLeftRight => arrow_left_right(), + Icons::ArrowsContract => arrows_angle_contract(), + Icons::ArrowsExpand => arrows_angle_expand(), + Icons::AtSymbol => at(), + // Icons::BandAid => Bandaid, + Icons::Cash => cash(), + // Icons::BarChart => BarChart, + // Icons::BarCode => , + Icons::Battery => battery(), + Icons::BatteryCharging => battery_charging(), + // Icons::BatteryDisabled => , + Icons::Bell => bell(), + Icons::BellDisabled => bell_slash(), + // Icons::Bike => Bicycle, + // Icons::Binoculars => Binoculars, + // Icons::Bird => , + Icons::Bluetooth => bluetooth(), + // Icons::Boat => , + Icons::Bold => type_bold(), + // Icons::Bolt => , + // Icons::BoltDisabled => , + Icons::Book => book(), + Icons::Bookmark => bookmark(), + Icons::Box => r#box(), + // Icons::Brush => Brush, + Icons::Bug => bug(), + Icons::Building => building(), + Icons::BulletPoints => list_ul(), + Icons::Calculator => calculator(), + Icons::Calendar => calendar(), + Icons::Camera => camera(), + Icons::Car => car_front(), + Icons::Cart => cart(), + // Icons::Cd => + // Icons::Center => + Icons::Checkmark => checktwo(), + // Icons::ChessPiece => + Icons::ChevronDown => chevron_down(), + Icons::ChevronLeft => chevron_left(), + Icons::ChevronRight => chevron_right(), + Icons::ChevronUp => chevron_up(), + Icons::ChevronExpand => chevron_expand(), + Icons::Circle => circle(), + // Icons::CircleProgress100 => + // Icons::CircleProgress25 => + // Icons::CircleProgress50 => + // Icons::CircleProgress75 => + // Icons::ClearFormatting => + Icons::Clipboard => clipboard(), + Icons::Clock => clock(), + Icons::Cloud => cloud(), + Icons::CloudLightning => cloud_lightning(), + Icons::CloudRain => cloud_rain(), + Icons::CloudSnow => cloud_snow(), + Icons::CloudSun => cloud_sun(), + Icons::Code => code(), + Icons::Gear => gear(), + Icons::Coin => coin(), + Icons::Command => command(), + Icons::Compass => compass(), + // Icons::ComputerChip => + // Icons::Contrast => + Icons::CreditCard => credit_card(), + Icons::Crop => crop(), + // Icons::Crown => + Icons::Document => file_earmark(), + Icons::DocumentAdd => file_earmark_plus(), + Icons::DocumentDelete => file_earmark_x(), + Icons::Dot => dot(), + Icons::Download => download(), + // Icons::Duplicate => , + Icons::Eject => eject(), + Icons::ThreeDots => three_dots(), + Icons::Envelope => envelope(), + Icons::Eraser => eraser(), + Icons::ExclamationMark => exclamation_lg(), + Icons::Eye => eye(), + Icons::EyeDisabled => eye_slash(), + Icons::EyeDropper => eyedropper(), + Icons::Female => gender_female(), + Icons::Film => film(), + Icons::Filter => filter(), + Icons::Fingerprint => fingerprint(), + Icons::Flag => flag(), + Icons::Folder => folder(), + Icons::FolderAdd => folder_plus(), + Icons::FolderDelete => folder_minus(), + Icons::Forward => forward(), + Icons::GameController => controller(), + Icons::Virus => virus(), + Icons::Gift => gift(), + Icons::Glasses => eyeglasses(), + Icons::Globe => globe(), + Icons::Hammer => hammer(), + Icons::HardDrive => device_hdd(), + Icons::Headphones => headphones(), + Icons::Heart => heart(), + // Icons::HeartDisabled => , + Icons::Heartbeat => activity(), + Icons::Hourglass => hourglass(), + Icons::House => house(), + Icons::Image => image(), + Icons::Info => info_lg(), + Icons::Italics => type_italic(), + Icons::Key => key(), + Icons::Keyboard => keyboard(), + Icons::Layers => layers(), + // Icons::Leaf => , + Icons::LightBulb => lightbulb(), + Icons::LightBulbDisabled => lightbulb_off(), + Icons::Link => link_fourfivedeg(), + Icons::List => list(), + Icons::Lock => lock(), + // Icons::LockDisabled => , + Icons::LockUnlocked => unlock(), + // Icons::Logout => , + // Icons::Lowercase => , + // Icons::MagnifyingGlass => , + Icons::Male => gender_male(), + Icons::Map => map(), + Icons::Maximize => fullscreen(), + Icons::Megaphone => megaphone(), + Icons::MemoryModule => memory(), + Icons::MemoryStick => usb_drive(), + Icons::Message => chat(), + Icons::Microphone => mic(), + Icons::MicrophoneDisabled => mic_mute(), + Icons::Minimize => fullscreen_exit(), + Icons::Minus => dash(), + Icons::Mobile => phone(), + // Icons::Monitor => , + Icons::Moon => moon(), + // Icons::Mountain => , + Icons::Mouse => mouse(), + Icons::Multiply => x(), + Icons::Music => music_note_beamed(), + Icons::Network => broadcast_pin(), + Icons::Paperclip => paperclip(), + Icons::Paragraph => text_paragraph(), + Icons::Pause => pause(), + Icons::Pencil => pencil(), + Icons::Person => person(), + Icons::PersonAdd => person_add(), + Icons::PersonRemove => person_dash(), + Icons::Phone => telephone(), + // Icons::PhoneRinging => , + Icons::PieChart => pie_chart(), + Icons::Capsule => capsule(), + // Icons::Pin => , + // Icons::PinDisabled => , + Icons::Play => play(), + Icons::Plug => plug(), + Icons::Plus => plus(), + // Icons::PlusMinusDivideMultiply => , + Icons::Power => power(), + Icons::Printer => printer(), + Icons::QuestionMark => question_lg(), + Icons::Quotes => quote(), + Icons::Receipt => receipt(), + Icons::Repeat => repeat(), + Icons::Reply => reply(), + Icons::Rewind => rewind(), + Icons::Rocket => rocket(), + // Icons::Ruler => , + Icons::Shield => shield(), + Icons::Shuffle => shuffle(), + Icons::Snippets => body_text(), + Icons::Snowflake => snow(), + // Icons::VolumeHigh => VolumeUp, + // Icons::VolumeLow => VolumeDown, + // Icons::VolumeOff => VolumeOff, + // Icons::VolumeOn => , + Icons::Star => star(), + // Icons::StarDisabled => , + Icons::Stop => stop(), + Icons::Stopwatch => stopwatch(), + Icons::StrikeThrough => type_strikethrough(), + Icons::Sun => sun_fill(), // TODO why Sun isn't in iced_aw? + Icons::Scissors => scissors(), + // Icons::Syringe => , + Icons::Tag => tag(), + Icons::Thermometer => thermometer(), + Icons::Terminal => terminal(), + Icons::Text => fonts(), + Icons::TextCursor => cursor_text(), + // Icons::TextSelection => , + // Icons::Torch => , + // Icons::Train => , + Icons::Trash => trash(), + Icons::Tree => tree(), + Icons::Trophy => trophy(), + Icons::People => people(), + Icons::Umbrella => umbrella(), + Icons::Underline => type_underline(), + Icons::Upload => upload(), + // Icons::Uppercase => , + Icons::Wallet => wallet(), + Icons::Wand => magic(), + // Icons::Warning => , + // Icons::Weights => , + Icons::Wifi => wifi(), + Icons::WifiDisabled => wifi_off(), + Icons::Window => window(), + Icons::Tools => tools(), + Icons::Watch => watch(), + Icons::XMark => x_lg(), + Icons::Indent => indent(), + Icons::Unindent => unindent(), } } diff --git a/rust/client/src/ui/widget/inline.rs b/rust/client/src/ui/widget/inline.rs index d674e93..84132a9 100644 --- a/rust/client/src/ui/widget/inline.rs +++ b/rust/client/src/ui/widget/inline.rs @@ -9,9 +9,7 @@ use iced::widget::column; use iced::widget::container; use iced::widget::row; use iced::widget::text; -use iced::widget::value; use iced::widget::vertical_rule; -use iced_fonts::BOOTSTRAP_FONT; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; @@ -30,10 +28,7 @@ impl<'b> ComponentWidgets<'b> { let top_rule = container(top_rule).align_x(Horizontal::Center).into(); - let icon = value(icon_to_bootstrap(icon)) - .font(BOOTSTRAP_FONT) - .size(45) - .themed(TextStyle::InlineSeparator); + let icon = icon_to_bootstrap(icon).size(45).themed(TextStyle::InlineSeparator); let bot_rule: Element<_> = vertical_rule(1).into(); diff --git a/rust/client/src/ui/widget/metadata.rs b/rust/client/src/ui/widget/metadata.rs index b26d463..9be50ff 100644 --- a/rust/client/src/ui/widget/metadata.rs +++ b/rust/client/src/ui/widget/metadata.rs @@ -20,9 +20,7 @@ use iced::widget::scrollable; use iced::widget::text; use iced::widget::tooltip; use iced::widget::tooltip::Position; -use iced::widget::value; -use iced_fonts::BOOTSTRAP_FONT; -use iced_fonts::Bootstrap; +use iced_fonts::bootstrap::box_arrow_up_right; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; @@ -78,7 +76,7 @@ impl<'b> ComponentWidgets<'b> { ) -> Element<'a, ComponentWidgetEvent> { let content: Element<_> = self.render_text(&widget.content.text, TextRenderType::None); - let icon: Element<_> = value(Bootstrap::BoxArrowUpRight).font(BOOTSTRAP_FONT).size(16).into(); + let icon: Element<_> = box_arrow_up_right().size(16).into(); let icon = container(icon).themed(ContainerStyle::MetadataLinkIcon); @@ -117,10 +115,7 @@ impl<'b> ComponentWidgets<'b> { widget: &MetadataIconWidget, is_in_list: bool, ) -> Element<'a, ComponentWidgetEvent> { - let value = value(icon_to_bootstrap(&widget.icon)) - .font(BOOTSTRAP_FONT) - .size(26) - .into(); + let value = icon_to_bootstrap(&widget.icon).size(26).into(); render_metadata_item(&widget.label, value, is_in_list).into() } diff --git a/rust/client/src/ui/widget/root.rs b/rust/client/src/ui/widget/root.rs index 9004047..933edcd 100644 --- a/rust/client/src/ui/widget/root.rs +++ b/rust/client/src/ui/widget/root.rs @@ -19,10 +19,8 @@ use iced::widget::mouse_area; use iced::widget::row; use iced::widget::stack; use iced::widget::text; -use iced::widget::value; use iced::widget::vertical_rule; -use iced_fonts::BOOTSTRAP_FONT; -use iced_fonts::Bootstrap; +use iced_fonts::bootstrap::arrow_left; use crate::ui::custom_widgets::loading_bar::LoadingBar; use crate::ui::scroll_handle::ScrollHandle; @@ -127,7 +125,7 @@ impl<'b> ComponentWidgets<'b> { } fn render_top_panel<'a>(&self, search_bar: &Option) -> Element<'a, ComponentWidgetEvent> { - let icon = value(Bootstrap::ArrowLeft).font(BOOTSTRAP_FONT); + let icon = arrow_left(); let back_button: Element<_> = button(icon) .on_press(ComponentWidgetEvent::PreviousView) diff --git a/rust/client/src/ui/widget/state.rs b/rust/client/src/ui/widget/state.rs index d817ead..dd1cf27 100644 --- a/rust/client/src/ui/widget/state.rs +++ b/rust/client/src/ui/widget/state.rs @@ -7,7 +7,6 @@ use gauntlet_common::model::RootWidget; use gauntlet_common::model::RootWidgetMembers; use gauntlet_common::model::UiWidgetId; use iced::widget::text_input; -use iced_aw::date_picker::Date; use crate::ui::scroll_handle::ESTIMATED_MAIN_LIST_ITEM_HEIGHT; use crate::ui::scroll_handle::ScrollHandle; @@ -37,9 +36,6 @@ pub fn create_state(root_widget: &RootWidget) -> HashMap { result.insert(widget.__id__, ComponentWidgetState::checkbox(&widget.value)); } - FormWidgetOrderedMembers::DatePicker(widget) => { - result.insert(widget.__id__, ComponentWidgetState::date_picker(&widget.value)); - } FormWidgetOrderedMembers::Select(widget) => { result.insert(widget.__id__, ComponentWidgetState::select(&widget.value)); } @@ -112,7 +108,6 @@ pub fn create_state(root_widget: &RootWidget) -> HashMap, @@ -166,41 +155,9 @@ impl ComponentWidgetState { }) } - fn date_picker(value: &Option) -> ComponentWidgetState { - let value = value - .to_owned() - .map(|value| parse_date(&value)) - .flatten() - .map(|(year, month, day)| Date::from_ymd(year, month, day)) - .unwrap_or(Date::today()); - - ComponentWidgetState::DatePicker(DatePickerState { - state_value: value, - show_picker: false, - }) - } - fn select(value: &Option) -> ComponentWidgetState { ComponentWidgetState::Select(SelectState { state_value: value.to_owned(), }) } } - -fn parse_date(value: &str) -> Option<(i32, u32, u32)> { - let ymd: Vec<_> = value.split("-").collect(); - - match ymd[..] { - [year, month, day] => { - let year = year.parse::(); - let month = month.parse::(); - let day = day.parse::(); - - match (year, month, day) { - (Ok(year), Ok(month), Ok(day)) => Some((year, month, day)), - _ => None, - } - } - _ => None, - } -} diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml index d17d09f..1afbea7 100644 --- a/rust/common/Cargo.toml +++ b/rust/common/Cargo.toml @@ -9,6 +9,7 @@ gauntlet-utils-macros.workspace = true # shared anyhow.workspace = true +tracing.workspace = true tokio.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/rust/common/src/cli.rs b/rust/common/src/cli.rs new file mode 100644 index 0000000..3812817 --- /dev/null +++ b/rust/common/src/cli.rs @@ -0,0 +1,109 @@ +use gauntlet_utils::channel::RequestError; + +use crate::model::EntrypointId; +use crate::model::PluginId; +use crate::rpc::backend_api::BackendForCliApi; +use crate::rpc::backend_api::BackendForCliApiProxy; +use crate::rpc::backend_api::GrpcBackendApi; + +pub fn is_server_running() -> bool { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("unable to start server tokio runtime") + .block_on(async { + let test_fn = || { + async { + let api = GrpcBackendApi::new().await?; + + let api = BackendForCliApiProxy::new(api); + + api.ping().await?; + + anyhow::Ok(()) + } + }; + + test_fn().await.is_ok() + }) +} + +pub fn open_window() { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("unable to start server tokio runtime") + .block_on(async { + let result = GrpcBackendApi::new().await; + + match result { + Ok(backend_api) => { + let backend_api = BackendForCliApiProxy::new(backend_api); + + tracing::info!("Server is already running, opening window..."); + + backend_api.show_window().await.expect("Unknown error") + } + Err(_) => { + tracing::error!("Unable to connect to server. Please check if you have Gauntlet running on your PC") + } + } + }) +} + +pub fn open_settings_window() { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("unable to start server tokio runtime") + .block_on(async { + let result = GrpcBackendApi::new().await; + + match result { + Ok(backend_api) => { + let backend_api = BackendForCliApiProxy::new(backend_api); + + backend_api.show_settings_window().await.expect("Unknown error") + } + Err(_) => { + tracing::error!("Unable to connect to server. Please check if you have Gauntlet running on your PC") + } + } + }) +} + +pub fn run_action(plugin_id: String, entrypoint_id: String, action_id: String) { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("unable to start server tokio runtime") + .block_on(async { + let result = GrpcBackendApi::new().await; + + match result { + Ok(backend_api) => { + let backend_api = BackendForCliApiProxy::new(backend_api); + + let plugin_id = PluginId::from_string(plugin_id); + let entrypoint_id = EntrypointId::from_string(entrypoint_id); + + if let Err(err) = backend_api.run_action(plugin_id, entrypoint_id, action_id).await { + match err { + RequestError::Timeout => { + tracing::error!("Timeout occurred when handling command"); + } + RequestError::Other { display: value } => { + tracing::error!("Error occurred when handling command: {}", value); + } + RequestError::OtherSideWasDropped => { + tracing::error!("Error occurred when handling command: Other side was dropped"); + } + } + } + } + Err(_) => { + tracing::error!("Unable to connect to server. Please check if you have Gauntlet running on your PC") + } + } + }) +} diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs index b6d78f4..ea8b1bb 100644 --- a/rust/common/src/lib.rs +++ b/rust/common/src/lib.rs @@ -1,6 +1,7 @@ use serde::Deserialize; use serde::Serialize; +pub mod cli; pub mod detached_process; pub mod dirs; pub mod model; diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index 5336fd3..baaff65 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -217,8 +217,6 @@ pub struct UiTheme { pub struct UiSetupData { pub window_position_file: Option, pub theme: UiTheme, - pub global_shortcut: Option, - pub global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), PhysicalShortcut>, pub close_on_unfocus: bool, pub window_position_mode: WindowPositionMode, } @@ -366,7 +364,6 @@ pub trait WidgetVisitor { async fn text_field_widget(&mut self, _widget: &TextFieldWidget) {} async fn password_field_widget(&mut self, _widget: &PasswordFieldWidget) {} async fn checkbox_widget(&mut self, _widget: &CheckboxWidget) {} - async fn date_picker_widget(&mut self, _widget: &DatePickerWidget) {} async fn select_item_widget(&mut self, _widget: &SelectItemWidget) {} async fn select_widget(&mut self, widget: &SelectWidget) { for members in &widget.content.ordered_members { @@ -385,7 +382,6 @@ pub trait WidgetVisitor { FormWidgetOrderedMembers::TextField(widget) => self.text_field_widget(widget).await, FormWidgetOrderedMembers::PasswordField(widget) => self.password_field_widget(widget).await, FormWidgetOrderedMembers::Checkbox(widget) => self.checkbox_widget(widget).await, - FormWidgetOrderedMembers::DatePicker(widget) => self.date_picker_widget(widget).await, FormWidgetOrderedMembers::Select(widget) => self.select_widget(widget).await, FormWidgetOrderedMembers::Separator(widget) => self.separator_widget(widget).await, } diff --git a/rust/common/src/rpc/backend_api.rs b/rust/common/src/rpc/backend_api.rs index ec01eb2..0783b28 100644 --- a/rust/common/src/rpc/backend_api.rs +++ b/rust/common/src/rpc/backend_api.rs @@ -19,7 +19,6 @@ use crate::model::SearchResult; use crate::model::SettingsPlugin; use crate::model::SettingsTheme; use crate::model::UiPropertyValue; -use crate::model::UiSetupData; use crate::model::UiWidgetId; use crate::model::WindowPositionMode; use crate::rpc::grpc::RpcBincode; @@ -29,14 +28,6 @@ use crate::rpc::grpc::rpc_backend_client::RpcBackendClient; #[allow(async_fn_in_trait)] #[boundary_gen(in_process)] pub trait BackendForFrontendApi { - async fn setup_data(&self) -> RequestResult; - - async fn setup_response( - &self, - global_shortcut_error: Option, - global_entrypoint_shortcuts_errors: HashMap<(PluginId, EntrypointId), Option>, - ) -> RequestResult<()>; - async fn search(&self, text: String, render_inline_view: bool) -> RequestResult>; async fn request_view_render( diff --git a/rust/common/src/rpc/frontend_api.rs b/rust/common/src/rpc/frontend_api.rs index cd33e32..4dc3e3d 100644 --- a/rust/common/src/rpc/frontend_api.rs +++ b/rust/common/src/rpc/frontend_api.rs @@ -4,7 +4,6 @@ use gauntlet_utils::channel::RequestResult; use gauntlet_utils_macros::boundary_gen; use crate::model::EntrypointId; -use crate::model::PhysicalShortcut; use crate::model::PluginId; use crate::model::RootWidget; use crate::model::UiRenderLocation; @@ -59,15 +58,6 @@ pub trait FrontendApi { show: bool, ) -> RequestResult<()>; - async fn set_global_shortcut(&self, shortcut: Option) -> RequestResult<()>; - - async fn set_global_entrypoint_shortcut( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - shortcut: Option, - ) -> RequestResult<()>; - async fn set_theme(&self, theme: UiTheme) -> RequestResult<()>; async fn set_window_position_mode(&self, mode: WindowPositionMode) -> RequestResult<()>; diff --git a/rust/common_ui/Cargo.toml b/rust/common_ui/Cargo.toml index 5bf4da6..197933e 100644 --- a/rust/common_ui/Cargo.toml +++ b/rust/common_ui/Cargo.toml @@ -8,5 +8,4 @@ gauntlet-common.workspace = true # shared iced.workspace = true -iced_aw.workspace = true iced_fonts.workspace = true diff --git a/rust/common_ui/src/lib.rs b/rust/common_ui/src/lib.rs index 4f8b087..705dde3 100644 --- a/rust/common_ui/src/lib.rs +++ b/rust/common_ui/src/lib.rs @@ -6,9 +6,11 @@ use iced::Pixels; use iced::border::Radius; use iced::keyboard::Modifiers; use iced::widget::text; -use iced::widget::value; -use iced_aw::iced_fonts::BOOTSTRAP_FONT; -use iced_aw::iced_fonts::Bootstrap; +use iced_fonts::BOOTSTRAP_FONT; +use iced_fonts::bootstrap::arrow_return_left; +use iced_fonts::bootstrap::command; +use iced_fonts::bootstrap::option; +use iced_fonts::bootstrap::shift; pub fn padding( top: impl Into, @@ -49,7 +51,7 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( let (key_name, show_shift) = match shortcut.physical_key { PhysicalKey::Enter => { let key_name = if cfg!(target_os = "macos") { - value(Bootstrap::ArrowReturnLeft).font(BOOTSTRAP_FONT).into() + arrow_return_left().into() } else { text("Enter").into() }; @@ -67,7 +69,7 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( let alt_modifier_text = if shortcut.modifier_alt { if cfg!(target_os = "macos") { - Some(value(Bootstrap::Option).font(BOOTSTRAP_FONT).into()) + Some(option().into()) } else { Some(text("Alt").into()) } @@ -77,7 +79,7 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( let meta_modifier_text = if shortcut.modifier_meta { if cfg!(target_os = "macos") { - Some(value(Bootstrap::Command).font(BOOTSTRAP_FONT).into()) + Some(command().into()) } else if cfg!(target_os = "windows") { Some( text("Win") // is it possible to have shortcuts that use win? @@ -94,7 +96,7 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( if cfg!(target_os = "macos") { Some( text("^") // TODO bootstrap doesn't have proper macos ctrl icon - .font(BOOTSTRAP_FONT) + .font(BOOTSTRAP_FONT) // todo replace .into(), ) } else { @@ -106,7 +108,7 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( let shift_modifier_text = if show_shift && shortcut.modifier_shift { if cfg!(target_os = "macos") { - Some(value(Bootstrap::Shift).font(BOOTSTRAP_FONT).into()) + Some(shift().into()) } else { Some(text("Shift").into()) } diff --git a/rust/component_model/src/lib.rs b/rust/component_model/src/lib.rs index 9df30ab..e65fb7e 100644 --- a/rust/component_model/src/lib.rs +++ b/rust/component_model/src/lib.rs @@ -1047,32 +1047,32 @@ pub fn create_component_model() -> Vec { children_none(), ); - let date_picker_component = component( - "date_picker", - mark_doc!("/date_picker/description.md"), - "DatePicker", - [ - property( - "label", - mark_doc!("/date_picker/props/label.md"), - true, - PropertyType::String, - ), - property( - "value", - mark_doc!("/date_picker/props/value.md"), - true, - PropertyType::String, - ), - event( - "onChange", - mark_doc!("/date_picker/props/onChange.md"), - true, - [property("value", "".to_string(), true, PropertyType::String)], - ), - ], - children_none(), - ); + // let date_picker_component = component( + // "date_picker", + // mark_doc!("/date_picker/description.md"), + // "DatePicker", + // [ + // property( + // "label", + // mark_doc!("/date_picker/props/label.md"), + // true, + // PropertyType::String, + // ), + // property( + // "value", + // mark_doc!("/date_picker/props/value.md"), + // true, + // PropertyType::String, + // ), + // event( + // "onChange", + // mark_doc!("/date_picker/props/onChange.md"), + // true, + // [property("value", "".to_string(), true, PropertyType::String)], + // ), + // ], + // children_none(), + // ); let select_item_component = component( "select_item", @@ -1143,7 +1143,7 @@ pub fn create_component_model() -> Vec { member("PasswordField", &password_field_component, Arity::ZeroOrMore), // member("TextArea", &text_area_component), member("Checkbox", &checkbox_component, Arity::ZeroOrMore), - member("DatePicker", &date_picker_component, Arity::ZeroOrMore), + // member("DatePicker", &date_picker_component, Arity::ZeroOrMore), member("Select", &select_component, Arity::ZeroOrMore), // member("MultiSelect", &multi_select_component), member("Separator", &separator_component, Arity::ZeroOrMore), @@ -1599,7 +1599,7 @@ pub fn create_component_model() -> Vec { password_field_component, // text_area_component, checkbox_component, - date_picker_component, + // date_picker_component, select_item_component, select_component, // multi_select_component, diff --git a/rust/management_client/Cargo.toml b/rust/management_client/Cargo.toml index 05b7b69..15dfb9c 100644 --- a/rust/management_client/Cargo.toml +++ b/rust/management_client/Cargo.toml @@ -11,8 +11,6 @@ gauntlet-utils.workspace = true # shared anyhow.workspace = true iced.workspace = true -iced_aw.workspace = true -iced_table.workspace = true iced_fonts.workspace = true tracing.workspace = true tracing-subscriber.workspace = true diff --git a/rust/management_client/src/components/mod.rs b/rust/management_client/src/components/mod.rs index b77b782..7988ab2 100644 --- a/rust/management_client/src/components/mod.rs +++ b/rust/management_client/src/components/mod.rs @@ -1 +1,2 @@ pub mod shortcut_selector; +pub mod spinner; diff --git a/rust/management_client/src/components/shortcut_selector.rs b/rust/management_client/src/components/shortcut_selector.rs index b90d607..277ea19 100644 --- a/rust/management_client/src/components/shortcut_selector.rs +++ b/rust/management_client/src/components/shortcut_selector.rs @@ -14,7 +14,6 @@ use iced::advanced::Clipboard; use iced::advanced::Layout; use iced::advanced::Shell; use iced::advanced::Widget; -use iced::advanced::graphics::core::event; use iced::advanced::graphics::core::keyboard; use iced::advanced::layout; use iced::advanced::mouse; @@ -32,9 +31,7 @@ use iced::widget::row; use iced::widget::text; use iced::widget::tooltip; use iced::widget::tooltip::Position; -use iced::widget::value; -use iced_fonts::BOOTSTRAP_FONT; -use iced_fonts::Bootstrap; +use iced_fonts::bootstrap::exclamation_triangle_fill; use crate::theme::Element; use crate::theme::GauntletSettingsTheme; @@ -46,7 +43,7 @@ pub struct ShortcutData { pub error: Option, } -pub fn shortcut_selector<'a, 'b: 'a, 'c, Message: 'a, F>( +pub fn shortcut_selector<'a, 'b, Message: 'a, F>( current_shortcut: &'b ShortcutData, on_shortcut_captured: F, overlay_class: ::Class<'a>, @@ -201,17 +198,17 @@ impl<'a, 'b, Message: 'a> Widget for S tree.diff_children(&[self.content.as_widget(), self.popup.as_widget()]); } - fn on_event( + fn update( &mut self, tree: &mut Tree, - event: Event, + event: &Event, layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, - ) -> event::Status { + ) { let state = tree.state.downcast_mut::(); match &event { @@ -246,8 +243,7 @@ impl<'a, 'b, Message: 'a> Widget for S let message = (self.on_shortcut_captured)(Some(shortcut)); shell.publish(message); - - return event::Status::Captured; + shell.capture_event(); } } } @@ -271,6 +267,7 @@ impl<'a, 'b, Message: 'a> Widget for S } mouse::Event::CursorMoved { .. } => { state.is_hovering = cursor.is_over(layout.bounds()); + shell.request_redraw(); } _ => {} } @@ -278,7 +275,7 @@ impl<'a, 'b, Message: 'a> Widget for S _ => {} }; - self.content.as_widget_mut().on_event( + self.content.as_widget_mut().update( &mut tree.children[0], event, layout, @@ -287,7 +284,7 @@ impl<'a, 'b, Message: 'a> Widget for S clipboard, shell, viewport, - ) + ); } fn mouse_interaction( @@ -308,18 +305,19 @@ impl<'a, 'b, Message: 'a> Widget for S fn overlay<'c>( &'c mut self, tree: &'c mut Tree, - layout: Layout<'_>, + layout: Layout<'c>, renderer: &Renderer, + viewport: &Rectangle, translation: Vector, ) -> Option> { let state = tree.state.downcast_ref::(); let mut children = tree.children.iter_mut(); - let content = self - .content - .as_widget_mut() - .overlay(children.next().unwrap(), layout, renderer, translation); + let content = + self.content + .as_widget_mut() + .overlay(children.next().unwrap(), layout, renderer, viewport, translation); let popup = if state.is_capturing { Some(overlay::Element::new(Box::new(Overlay { @@ -404,10 +402,7 @@ pub fn render_shortcut<'a, Message: 'a>(shortcut: &ShortcutData, in_table: bool) } pub fn render_shortcut_error<'a, Message: 'a>(current_shortcut_error: String) -> Element<'a, Message> { - let error_icon: Element<_> = value(Bootstrap::ExclamationTriangleFill) - .font(BOOTSTRAP_FONT) - .class(TextStyle::Destructive) - .into(); + let error_icon: Element<_> = exclamation_triangle_fill().class(TextStyle::Destructive).into(); let error_text: Element<_> = text(current_shortcut_error).class(TextStyle::Destructive).into(); diff --git a/rust/management_client/src/components/spinner.rs b/rust/management_client/src/components/spinner.rs new file mode 100644 index 0000000..543d384 --- /dev/null +++ b/rust/management_client/src/components/spinner.rs @@ -0,0 +1,208 @@ +use iced::Border; +use iced::Color; +use iced::Element; +use iced::Event; +use iced::Length; +use iced::Rectangle; +use iced::Shadow; +use iced::Size; +use iced::Vector; +use iced::advanced::Clipboard; +use iced::advanced::Layout; +use iced::advanced::Shell; +use iced::advanced::Widget; +use iced::advanced::layout::Limits; +use iced::advanced::layout::Node; +use iced::advanced::renderer; +use iced::advanced::widget::Tree; +use iced::advanced::widget::tree::State; +use iced::advanced::widget::tree::Tag; +use iced::mouse::Cursor; +use iced::time::Duration; +use iced::time::Instant; +use iced::window; + +pub struct Spinner { + width: Length, + height: Length, + rate: Duration, + circle_radius: f32, +} + +impl Default for Spinner { + fn default() -> Self { + Self { + width: Length::Fixed(20.0), + height: Length::Fixed(20.0), + rate: Duration::from_secs_f32(1.0), + circle_radius: 2.0, + } + } +} + +impl Spinner { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + + #[must_use] + pub fn height(mut self, height: impl Into) -> Self { + self.height = height.into(); + self + } + + #[must_use] + pub fn circle_radius(mut self, radius: f32) -> Self { + self.circle_radius = radius; + self + } +} + +struct SpinnerState { + last_update: Instant, + t: f32, +} + +fn is_visible(bounds: &Rectangle) -> bool { + bounds.width > 0.0 && bounds.height > 0.0 +} + +fn fill_circle(renderer: &mut impl renderer::Renderer, position: Vector, radius: f32, color: Color) { + if radius > 0. { + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: position.x, + y: position.y, + width: radius * 2.0, + height: radius * 2.0, + }, + border: Border { + radius: radius.into(), + width: 0.0, + color: Color::TRANSPARENT, + }, + shadow: Shadow::default(), + snap: false, + }, + color, + ); + } +} + +impl Widget for Spinner +where + Renderer: renderer::Renderer, +{ + fn size(&self) -> Size { + Size::new(self.width, self.height) + } + + fn layout(&self, _tree: &mut Tree, _renderer: &Renderer, limits: &Limits) -> Node { + Node::new(limits.width(self.width).height(self.height).resolve( + self.width, + self.height, + Size::new(f32::INFINITY, f32::INFINITY), + )) + } + + fn draw( + &self, + state: &Tree, + renderer: &mut Renderer, + _theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + _cursor: Cursor, + _viewport: &Rectangle, + ) { + let bounds = layout.bounds(); + + if !is_visible(&bounds) { + return; + } + + let size = if bounds.width < bounds.height { + bounds.width + } else { + bounds.height + } / 2.0; + let state = state.state.downcast_ref::(); + let center = bounds.center(); + let distance_from_center = size - self.circle_radius; + let (y, x) = (state.t * std::f32::consts::PI * 2.0).sin_cos(); + let position = Vector::new( + center.x + x * distance_from_center - self.circle_radius, + center.y + y * distance_from_center - self.circle_radius, + ); + + fill_circle(renderer, position, self.circle_radius, style.text_color); + } + + fn tag(&self) -> Tag { + Tag::of::() + } + + fn state(&self) -> State { + State::new(SpinnerState { + last_update: Instant::now(), + t: 0.0, + }) + } + + fn update( + &mut self, + state: &mut Tree, + event: &Event, + layout: Layout<'_>, + _cursor: Cursor, + _renderer: &Renderer, + _clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + _viewport: &Rectangle, + ) { + const FRAMES_PER_SECOND: u64 = 60; + + let bounds = layout.bounds(); + + if let Event::Window(window::Event::RedrawRequested(now)) = event { + if is_visible(&bounds) { + let state = state.state.downcast_mut::(); + let duration = (*now - state.last_update).as_secs_f32(); + let increment = if self.rate == Duration::ZERO { + 0.0 + } else { + duration * 1.0 / self.rate.as_secs_f32() + }; + + state.t += increment; + + if state.t > 1.0 { + state.t -= 1.0; + } + + shell.request_redraw_at(window::RedrawRequest::At( + *now + Duration::from_millis(1000 / FRAMES_PER_SECOND), + )); + state.last_update = now.clone(); + shell.capture_event(); + } + } + } +} + +impl<'a, Message, Theme, Renderer> From for Element<'a, Message, Theme, Renderer> +where + Renderer: renderer::Renderer + 'a, +{ + fn from(spinner: Spinner) -> Self { + Self::new(spinner) + } +} diff --git a/rust/management_client/src/theme.rs b/rust/management_client/src/theme.rs index d4d5d74..e3f1134 100644 --- a/rust/management_client/src/theme.rs +++ b/rust/management_client/src/theme.rs @@ -1,15 +1,14 @@ -use iced::application::Appearance; -use iced::application::DefaultStyle; +use iced::theme; +use iced::theme::Palette; +use iced::theme::Style; pub mod button; pub mod checkbox; pub mod container; -pub mod number_input; pub mod pick_list; pub mod rule; pub mod scrollable; pub mod shortcut_selector; -pub mod table; pub mod text; pub mod text_input; @@ -18,13 +17,17 @@ pub type Element<'a, Message> = iced::Element<'a, Message, GauntletSettingsTheme #[derive(Default)] pub struct GauntletSettingsTheme; -impl DefaultStyle for GauntletSettingsTheme { - fn default_style(&self) -> Appearance { - Appearance { +impl theme::Base for GauntletSettingsTheme { + fn base(&self) -> Style { + Style { background_color: BACKGROUND_DARKEST.to_iced(), text_color: TEXT_LIGHTEST.to_iced(), } } + + fn palette(&self) -> Option { + Some(Palette::FERRA) + } } // keep colors more or less in sync with main ui diff --git a/rust/management_client/src/theme/container.rs b/rust/management_client/src/theme/container.rs index 00f7095..692b5f3 100644 --- a/rust/management_client/src/theme/container.rs +++ b/rust/management_client/src/theme/container.rs @@ -13,6 +13,7 @@ pub enum ContainerStyle { Transparent, Box, TextInputMissingValue, + TableEvenRow, } impl container::Catalog for GauntletSettingsTheme { @@ -40,7 +41,7 @@ impl container::Catalog for GauntletSettingsTheme { let color = DANGER.to_iced(); Style { - background: Some(Color::new(color.r, color.g, color.b, 0.3).into()), + background: Some(Color::from_rgba(color.r, color.g, color.b, 0.3).into()), border: Border { color: TRANSPARENT.to_iced(), radius: 4.0.into(), @@ -49,6 +50,16 @@ impl container::Catalog for GauntletSettingsTheme { ..Default::default() } } + ContainerStyle::TableEvenRow => { + Style { + background: Some(BACKGROUND_DARKER.to_iced().into()), + border: Border { + radius: 4.0.into(), + ..Default::default() + }, + ..Default::default() + } + } } } } diff --git a/rust/management_client/src/theme/number_input.rs b/rust/management_client/src/theme/number_input.rs deleted file mode 100644 index a2a663e..0000000 --- a/rust/management_client/src/theme/number_input.rs +++ /dev/null @@ -1,55 +0,0 @@ -use iced_aw::number_input::Style; -use iced_aw::number_input::number_input; -use iced_aw::style::Status; - -use crate::theme::GauntletSettingsTheme; -use crate::theme::PRIMARY; -use crate::theme::PRIMARY_HOVERED; -use crate::theme::TEXT_DARKER; -use crate::theme::TEXT_LIGHTEST; - -impl number_input::ExtendedCatalog for GauntletSettingsTheme { - fn style(&self, class: &(), status: Status) -> Style { - number_input::Catalog::style(self, class, status) - } -} - -impl number_input::Catalog for GauntletSettingsTheme { - type Class<'a> = (); - - fn default<'a>() -> Self::Class<'a> { - () - } - - fn style(&self, _class: &Self::Class<'_>, status: Status) -> Style { - match status { - Status::Active => active(), - Status::Hovered => active(), // TODO proper style - Status::Pressed => pressed(), - Status::Disabled => disabled(), - Status::Focused => active(), // TODO proper style - Status::Selected => pressed(), // TODO proper style - } - } -} - -fn active() -> Style { - Style { - button_background: Some(PRIMARY.to_iced().into()), - icon_color: TEXT_DARKER.to_iced(), - } -} - -fn pressed() -> Style { - Style { - button_background: Some(PRIMARY_HOVERED.to_iced().into()), - icon_color: TEXT_DARKER.to_iced(), - } -} - -fn disabled() -> Style { - Style { - button_background: None, - icon_color: TEXT_LIGHTEST.to_iced(), - } -} diff --git a/rust/management_client/src/theme/pick_list.rs b/rust/management_client/src/theme/pick_list.rs index 7028f0b..bd6c159 100644 --- a/rust/management_client/src/theme/pick_list.rs +++ b/rust/management_client/src/theme/pick_list.rs @@ -27,12 +27,12 @@ fn pick_list_appearance(status: pick_list::Status) -> pick_list::Style { use iced::widget::pick_list::Status; let background_color = match status { - Status::Active | Status::Opened => PRIMARY.to_iced(), + Status::Active | Status::Opened { is_hovered: _ } => PRIMARY.to_iced(), Status::Hovered => PRIMARY_HOVERED.to_iced(), }; let text_color = match status { - Status::Active | Status::Opened => TEXT_DARKEST.to_iced(), + Status::Active | Status::Opened { is_hovered: _ } => TEXT_DARKEST.to_iced(), Status::Hovered => TEXT_DARKEST.to_iced(), }; diff --git a/rust/management_client/src/theme/rule.rs b/rust/management_client/src/theme/rule.rs index 45aaf15..ae9ea52 100644 --- a/rust/management_client/src/theme/rule.rs +++ b/rust/management_client/src/theme/rule.rs @@ -17,6 +17,7 @@ impl rule::Catalog for GauntletSettingsTheme { width: 1, radius: 0.0.into(), fill_mode: rule::FillMode::Full, + snap: false, } } } diff --git a/rust/management_client/src/theme/scrollable.rs b/rust/management_client/src/theme/scrollable.rs index fc79c84..fafed5f 100644 --- a/rust/management_client/src/theme/scrollable.rs +++ b/rust/management_client/src/theme/scrollable.rs @@ -27,7 +27,7 @@ impl scrollable::Catalog for GauntletSettingsTheme { }; match status { - Status::Active => { + Status::Active { .. } => { Style { container: container::Style::default(), vertical_rail: scrollbar, @@ -38,6 +38,7 @@ impl scrollable::Catalog for GauntletSettingsTheme { Status::Hovered { is_horizontal_scrollbar_hovered, is_vertical_scrollbar_hovered, + .. } => { let hovered_scrollbar = scrollable::Rail { scroller: scrollable::Scroller { @@ -65,6 +66,7 @@ impl scrollable::Catalog for GauntletSettingsTheme { Status::Dragged { is_horizontal_scrollbar_dragged, is_vertical_scrollbar_dragged, + .. } => { let dragged_scrollbar = scrollable::Rail { scroller: scrollable::Scroller { diff --git a/rust/management_client/src/theme/table.rs b/rust/management_client/src/theme/table.rs deleted file mode 100644 index 4c4b71c..0000000 --- a/rust/management_client/src/theme/table.rs +++ /dev/null @@ -1,56 +0,0 @@ -use iced::Border; -use iced::widget::container; - -use crate::theme::BACKGROUND_DARKER; -use crate::theme::GauntletSettingsTheme; -use crate::theme::TEXT_LIGHTEST; - -impl iced_table::Catalog for GauntletSettingsTheme { - type Style = (); - - fn header(&self, _: &Self::Style) -> container::Style { - container::Style { - text_color: Some(TEXT_LIGHTEST.to_iced()), - background: Some(BACKGROUND_DARKER.to_iced().into()), - border: Border { - radius: 4.0.into(), - ..Default::default() - }, - ..Default::default() - } - } - - fn footer(&self, _: &Self::Style) -> container::Style { - container::Style { - text_color: Some(TEXT_LIGHTEST.to_iced()), - background: Some(BACKGROUND_DARKER.to_iced().into()), - border: Border { - radius: 4.0.into(), - ..Default::default() - }, - ..Default::default() - } - } - - // TODO selected and hovered upstream - fn row(&self, _: &Self::Style, index: usize) -> container::Style { - let background = if index % 2 == 0 { - None - } else { - Some(BACKGROUND_DARKER.to_iced().into()) - }; - - container::Style { - background, - border: Border { - radius: 4.0.into(), - ..Default::default() - }, - ..Default::default() - } - } - - fn divider(&self, _: &Self::Style, _hovered: bool) -> container::Style { - container::Style { ..Default::default() } - } -} diff --git a/rust/management_client/src/theme/text_input.rs b/rust/management_client/src/theme/text_input.rs index 1015b36..cf58f28 100644 --- a/rust/management_client/src/theme/text_input.rs +++ b/rust/management_client/src/theme/text_input.rs @@ -28,7 +28,7 @@ impl text_input::Catalog for GauntletSettingsTheme { fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { match class { TextInputStyle::EntrypointAlias => { - let border = if let Status::Focused | Status::Hovered = status { + let border = if let Status::Focused { is_hovered: _ } | Status::Hovered = status { Border { radius: BUTTON_BORDER_RADIUS.into(), width: 2.0, @@ -71,7 +71,7 @@ impl text_input::Catalog for GauntletSettingsTheme { ..active } } - Status::Focused => { + Status::Focused { is_hovered: _ } => { Style { background: Background::Color(BACKGROUND_DARKER.to_iced().into()), ..active diff --git a/rust/management_client/src/ui.rs b/rust/management_client/src/ui.rs index 9921827..492ad1f 100644 --- a/rust/management_client/src/ui.rs +++ b/rust/management_client/src/ui.rs @@ -36,14 +36,15 @@ use iced::widget::row; use iced::widget::scrollable; use iced::widget::stack; use iced::widget::text; -use iced::widget::value; use iced::window; -use iced_aw::Spinner; -use iced_fonts::BOOTSTRAP_FONT; use iced_fonts::BOOTSTRAP_FONT_BYTES; -use iced_fonts::Bootstrap; +use iced_fonts::bootstrap::exclamation_triangle_fill; +use iced_fonts::bootstrap::gear_fill; +use iced_fonts::bootstrap::patch_check_fill; +use iced_fonts::bootstrap::puzzle_fill; use itertools::Itertools; +use crate::components::spinner::Spinner; use crate::theme::Element; use crate::theme::GauntletSettingsTheme; use crate::theme::button::ButtonStyle; @@ -57,19 +58,16 @@ use crate::views::plugins::ManagementAppPluginMsgOut; use crate::views::plugins::ManagementAppPluginsState; pub fn run() { - iced::application::( - "Gauntlet Settings", - update, - view, - ) - .window(window::Settings { - size: Size::new(1150.0, 700.0), - ..Default::default() - }) - .subscription(subscription) - .theme(|_| GauntletSettingsTheme::default()) - .run_with(new) - .expect("Unable to start settings application"); + iced::application::(new, update, view) + .title("Gauntlet Settings") + .window(window::Settings { + size: Size::new(1150.0, 700.0), + ..Default::default() + }) + .subscription(subscription) + .theme(|_| GauntletSettingsTheme::default()) + .run() + .expect("Unable to start settings application"); } struct ManagementAppModel { @@ -414,8 +412,7 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { SettingsView::Plugins => state.plugins_state.view().map(|msg| ManagementAppMsg::Plugin(msg)), }; - let icon_general: Element<_> = value(Bootstrap::GearFill) - .font(BOOTSTRAP_FONT) + let icon_general: Element<_> = gear_fill() .height(Length::Fill) .width(Length::Fill) .align_y(alignment::Vertical::Center) @@ -449,8 +446,7 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { let general_button: Element<_> = container(general_button).padding(8.0).into(); - let icon_plugins: Element<_> = value(Bootstrap::PuzzleFill) - .font(BOOTSTRAP_FONT) + let icon_plugins: Element<_> = puzzle_fill() .height(Length::Fill) .width(Length::Fill) .align_y(alignment::Vertical::Center) @@ -529,10 +525,9 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { download_info_icons.push(spinner); } if successful_count > 0 { - let icon: Element<_> = value(Bootstrap::PatchCheckFill) + let icon: Element<_> = patch_check_fill() .size(16) .align_y(alignment::Vertical::Center) - .font(BOOTSTRAP_FONT) .height(Length::Fill) .class(TextStyle::Positive) .into(); @@ -549,8 +544,7 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { download_info_icons.push(icon); } if error_count > 0 { - let icon: Element<_> = value(Bootstrap::ExclamationTriangleFill) - .font(BOOTSTRAP_FONT) + let icon: Element<_> = exclamation_triangle_fill() .height(Length::Fill) .align_y(alignment::Vertical::Center) .size(16) @@ -658,8 +652,7 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { .size(14) .into(); - let icon: Element<_> = value(Bootstrap::ExclamationTriangleFill) - .font(BOOTSTRAP_FONT) + let icon: Element<_> = exclamation_triangle_fill() .align_y(alignment::Vertical::Center) .size(32) .class(TextStyle::Destructive) @@ -690,10 +683,9 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { let plugin_id: Element<_> = container(plugin_id).padding(padding::bottom(16)).into(); - let icon: Element<_> = value(Bootstrap::PatchCheckFill) + let icon: Element<_> = patch_check_fill() .size(32) .align_y(alignment::Vertical::Center) - .font(BOOTSTRAP_FONT) .class(TextStyle::Positive) .into(); diff --git a/rust/management_client/src/views/plugins.rs b/rust/management_client/src/views/plugins.rs index d15eaa5..c5cc8ef 100644 --- a/rust/management_client/src/views/plugins.rs +++ b/rust/management_client/src/views/plugins.rs @@ -27,10 +27,8 @@ use iced::widget::scrollable; use iced::widget::text; use iced::widget::text::Shaping; use iced::widget::text_input; -use iced::widget::value; use iced::widget::vertical_rule; -use iced_fonts::BOOTSTRAP_FONT; -use iced_fonts::Bootstrap; +use iced_fonts::bootstrap::plus; use crate::theme::Element; use crate::theme::button::ButtonStyle; @@ -793,7 +791,7 @@ impl ManagementAppPluginsState { let top_button_text = if plugin_url.is_some() { text("Download plugin") } else { - value(Bootstrap::Plus).font(BOOTSTRAP_FONT) + plus() }; let top_button_text_container: Element<_> = container(top_button_text) @@ -826,6 +824,9 @@ impl ManagementAppPluginsState { let separator: Element<_> = vertical_rule(1).into(); + let table = container(table).width(Length::FillPortion(7)).into(); + let sidebar = container(sidebar).width(Length::FillPortion(3)).into(); + let content: Element<_> = row(vec![table, separator, sidebar]).into(); let content = container(content) @@ -888,6 +889,7 @@ struct SettingsGeneratorData { pub enum PluginPreferenceUserDataState { Number { value: Option, + new_value: Option, }, String { value: Option, @@ -904,7 +906,7 @@ pub enum PluginPreferenceUserDataState { }, ListOfNumbers { value: Option>, - new_value: f64, + new_value: Option, }, ListOfEnums { value: Option>, @@ -915,7 +917,9 @@ pub enum PluginPreferenceUserDataState { impl PluginPreferenceUserDataState { pub fn from_user_data(value: PluginPreferenceUserData) -> Self { match value { - PluginPreferenceUserData::Number { value } => PluginPreferenceUserDataState::Number { value }, + PluginPreferenceUserData::Number { value } => { + PluginPreferenceUserDataState::Number { value, new_value: None } + } PluginPreferenceUserData::String { value } => PluginPreferenceUserDataState::String { value }, PluginPreferenceUserData::Enum { value } => PluginPreferenceUserDataState::Enum { value }, PluginPreferenceUserData::Bool { value } => PluginPreferenceUserDataState::Bool { value }, @@ -926,7 +930,7 @@ impl PluginPreferenceUserDataState { } } PluginPreferenceUserData::ListOfNumbers { value } => { - PluginPreferenceUserDataState::ListOfNumbers { value, new_value: 0.0 } + PluginPreferenceUserDataState::ListOfNumbers { value, new_value: None } } PluginPreferenceUserData::ListOfEnums { value } => { PluginPreferenceUserDataState::ListOfEnums { value, new_value: None } @@ -936,7 +940,7 @@ impl PluginPreferenceUserDataState { pub fn to_user_data(self) -> PluginPreferenceUserData { match self { - PluginPreferenceUserDataState::Number { value } => PluginPreferenceUserData::Number { value }, + PluginPreferenceUserDataState::Number { value, .. } => PluginPreferenceUserData::Number { value }, PluginPreferenceUserDataState::String { value } => PluginPreferenceUserData::String { value }, PluginPreferenceUserDataState::Enum { value } => PluginPreferenceUserData::Enum { value }, PluginPreferenceUserDataState::Bool { value } => PluginPreferenceUserData::Bool { value }, diff --git a/rust/management_client/src/views/plugins/preferences.rs b/rust/management_client/src/views/plugins/preferences.rs index c5a700a..103519d 100644 --- a/rust/management_client/src/views/plugins/preferences.rs +++ b/rust/management_client/src/views/plugins/preferences.rs @@ -7,7 +7,6 @@ use gauntlet_common::model::PluginPreference; use iced::Length; use iced::Padding; use iced::padding; -use iced::widget; use iced::widget::button; use iced::widget::checkbox; use iced::widget::column; @@ -17,9 +16,8 @@ use iced::widget::row; use iced::widget::text; use iced::widget::text::Shaping; use iced::widget::text_input; -use iced_aw::number_input; -use iced_fonts::BOOTSTRAP_FONT; -use iced_fonts::Bootstrap; +use iced_fonts::bootstrap::dash; +use iced_fonts::bootstrap::plus; use crate::theme::Element; use crate::theme::button::ButtonStyle; @@ -104,34 +102,46 @@ pub fn preferences_ui<'a>( let input_field: Element<_> = match preference { PluginPreference::Number { default, .. } => { - let value = match user_data { - None => None, - Some(PluginPreferenceUserDataState::Number { value }) => value.to_owned(), + let (value, new_value) = match user_data { + None => (None, None), + Some(PluginPreferenceUserDataState::Number { value, new_value }) => { + (value.to_owned(), new_value.to_owned()) + } Some(_) => unreachable!(), }; + let invalid_value = new_value + .as_ref() + .map(|value| value.parse::().is_err()) + .unwrap_or(false); let missing = value.as_ref().or(default.as_ref()).is_none(); + let invalid = missing || invalid_value; - let value = value.or(default.to_owned()).unwrap_or_default(); + let value = new_value + .clone() + .or(default.map(|value| value.to_string())) + .unwrap_or_default(); - let input_field: Element<_> = number_input(value, f64::MIN..f64::MAX, std::convert::identity) + let input_field: Element<_> = text_input("Enter number...", &value) + .on_input(move |value| { + PluginPreferencesMsg::UpdatePreferenceValue { + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + id: preference_id.to_owned(), + user_data: PluginPreferenceUserDataState::Number { + value: value.parse().ok(), + new_value: Some(value.clone()), + }, + } + }) .width(Length::Fill) .into(); - let input_field = input_field.map(Box::new(move |value| { - PluginPreferencesMsg::UpdatePreferenceValue { - plugin_id: plugin_id.clone(), - entrypoint_id: entrypoint_id.clone(), - id: preference_id.to_owned(), - user_data: PluginPreferenceUserDataState::Number { value: Some(value) }, - } - })); - let input_field = container(input_field) .width(Length::Fill) .padding(Padding::from([4.0, 8.0])) .class( - if missing { + if invalid { ContainerStyle::TextInputMissingValue } else { ContainerStyle::Transparent @@ -295,7 +305,7 @@ pub fn preferences_ui<'a>( .padding(Padding::new(4.0)) .into(); - let remove_icon = widget::value(Bootstrap::Dash).font(BOOTSTRAP_FONT); + let remove_icon = dash(); let remove_button: Element<_> = button(remove_icon) .class(ButtonStyle::Primary) @@ -344,7 +354,7 @@ pub fn preferences_ui<'a>( }) }; - let add_icon: Element<_> = widget::value(Bootstrap::Plus).font(BOOTSTRAP_FONT).into(); + let add_icon: Element<_> = plus().into(); let add_button: Element<_> = button(add_icon) .class(ButtonStyle::Primary) @@ -391,7 +401,7 @@ pub fn preferences_ui<'a>( } PluginPreference::ListOfNumbers { default, .. } => { let (value, new_value) = match user_data { - None => (None, 0.0), + None => (None, None), Some(PluginPreferenceUserDataState::ListOfNumbers { value, new_value }) => { (value.to_owned(), new_value.to_owned()) } @@ -416,7 +426,7 @@ pub fn preferences_ui<'a>( .padding(Padding::new(4.0)) .into(); - let remove_icon = widget::value(Bootstrap::Dash).font(BOOTSTRAP_FONT); + let remove_icon = dash(); let remove_button: Element<_> = button(remove_icon) .class(ButtonStyle::Primary) @@ -442,16 +452,21 @@ pub fn preferences_ui<'a>( }) .collect(); + let save_new_value = new_value + .as_ref() + .map(|value| value.parse().ok()) + .flatten() + .unwrap_or_default(); let save_value = match &value { - None => vec![new_value.clone()], + None => vec![save_new_value], Some(value) => { let mut save_value = value.clone(); - save_value.push(new_value.clone()); + save_value.push(save_new_value); save_value } }; - let add_icon: Element<_> = widget::value(Bootstrap::Plus).font(BOOTSTRAP_FONT).into(); + let add_icon: Element<_> = plus().into(); let add_button: Element<_> = button(add_icon) .class(ButtonStyle::Primary) @@ -461,7 +476,7 @@ pub fn preferences_ui<'a>( id: preference_id.to_owned(), user_data: PluginPreferenceUserDataState::ListOfNumbers { value: Some(save_value), - new_value: 0.0, + new_value: None, }, }) .padding(Padding::from([5.0, 7.0])) @@ -469,22 +484,21 @@ pub fn preferences_ui<'a>( let add_button: Element<_> = container(add_button).padding(padding::bottom(8.0)).into(); - let add_number_input: Element<_> = number_input(new_value, f64::MIN..f64::MAX, std::convert::identity) + let add_number_input: Element<_> = text_input("Enter number...", &new_value.unwrap_or_default()) + .on_input(move |new_value| { + PluginPreferencesMsg::UpdatePreferenceValue { + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + id: preference_id.to_owned(), + user_data: PluginPreferenceUserDataState::ListOfNumbers { + value: value.clone(), + new_value: Some(new_value), + }, + } + }) .width(Length::Fill) .into(); - let add_number_input = add_number_input.map(Box::new(move |new_value| { - PluginPreferencesMsg::UpdatePreferenceValue { - plugin_id: plugin_id.clone(), - entrypoint_id: entrypoint_id.clone(), - id: preference_id.to_owned(), - user_data: PluginPreferenceUserDataState::ListOfNumbers { - value: value.clone(), - new_value, - }, - } - })); - let add_item: Element<_> = row([add_number_input, add_button]).into(); let add_item: Element<_> = container(add_item).padding(Padding::new(8.0)).into(); @@ -535,7 +549,7 @@ pub fn preferences_ui<'a>( .padding(Padding::new(4.0)) .into(); - let remove_icon = widget::value(Bootstrap::Dash).font(BOOTSTRAP_FONT); + let remove_icon = dash(); let remove_button: Element<_> = button(remove_icon) .class(ButtonStyle::Primary) @@ -585,7 +599,7 @@ pub fn preferences_ui<'a>( } }; - let add_icon: Element<_> = widget::value(Bootstrap::Plus).font(BOOTSTRAP_FONT).into(); + let add_icon: Element<_> = plus().into(); let add_button: Element<_> = button(add_icon) .class(ButtonStyle::Primary) diff --git a/rust/management_client/src/views/plugins/table.rs b/rust/management_client/src/views/plugins/table.rs index 5e0f5a4..4da26df 100644 --- a/rust/management_client/src/views/plugins/table.rs +++ b/rust/management_client/src/views/plugins/table.rs @@ -9,29 +9,25 @@ use gauntlet_common::model::SettingsEntrypointType; use gauntlet_common::model::SettingsPlugin; use iced::Alignment; use iced::Length; -use iced::Renderer; use iced::Task; use iced::advanced::text::Shaping; use iced::padding; use iced::widget::Space; use iced::widget::button; use iced::widget::checkbox; +use iced::widget::column; use iced::widget::container; use iced::widget::horizontal_space; use iced::widget::row; use iced::widget::scrollable; -use iced::widget::scrollable::Id; use iced::widget::text; use iced::widget::text_input; -use iced::widget::value; -use iced_fonts::BOOTSTRAP_FONT; -use iced_fonts::Bootstrap; -use iced_table::table; +use iced_fonts::bootstrap::caret_down; +use iced_fonts::bootstrap::caret_right; use crate::components::shortcut_selector::ShortcutData; use crate::components::shortcut_selector::shortcut_selector; use crate::theme::Element; -use crate::theme::GauntletSettingsTheme; use crate::theme::button::ButtonStyle; use crate::theme::container::ContainerStyle; use crate::theme::text_input::TextInputStyle; @@ -41,7 +37,6 @@ use crate::views::plugins::SettingsPluginData; #[derive(Debug, Clone)] pub enum PluginTableMsgIn { - TableSyncHeader(scrollable::AbsoluteOffset), SelectItem(SelectedItem), EnabledToggleItem(EnabledItem), ToggleShowEntrypoints { @@ -78,31 +73,16 @@ pub enum PluginTableMsgOut { } pub struct PluginTableState { - columns: Vec, rows: Vec, - header: Id, - body: Id, } impl PluginTableState { pub fn new() -> Self { - Self { - columns: vec![ - Column::new(ColumnKind::Name), - Column::new(ColumnKind::Type), - Column::new(ColumnKind::Alias), - Column::new(ColumnKind::Shortcut), - Column::new(ColumnKind::EnableToggle), - ], - rows: vec![], - header: Id::unique(), - body: Id::unique(), - } + Self { rows: vec![] } } pub fn update(&mut self, message: PluginTableMsgIn) -> Task { match message { - PluginTableMsgIn::TableSyncHeader(offset) => scrollable::scroll_to(self.header.clone(), offset), PluginTableMsgIn::EnabledToggleItem(item) => { match item { EnabledItem::Plugin { enabled, plugin_id } => { @@ -234,15 +214,42 @@ impl PluginTableState { } pub fn view(&self) -> Element { - table( - self.header.clone(), - self.body.clone(), - &self.columns, - &self.rows, - PluginTableMsgIn::TableSyncHeader, - ) - .cell_padding(0.0) - .into() + let mut rows: Vec> = self + .rows + .iter() + .enumerate() + .map(|(index, row_item)| { + let style = if index % 2 != 0 { + ContainerStyle::TableEvenRow + } else { + ContainerStyle::Transparent + }; + + table_row( + style, + name_cell(row_item), + type_cell(row_item), + shortcut_cell(row_item), + alias_cell(row_item), + enable_cell(row_item), + ) + }) + .collect(); + + let header = table_row( + ContainerStyle::TableEvenRow, + header_cell("Name", true), + header_cell("Type", false), + header_cell("Alias", false), + header_cell("Shortcut", false), + header_cell("Enabled", false), + ); + + rows.insert(0, header); + + let table: Element<_> = column(rows).into(); + + scrollable(table).into() } } @@ -281,494 +288,447 @@ enum Row { }, } -enum ColumnKind { - Name, - Type, - Shortcut, - Alias, - EnableToggle, +fn table_row<'a>( + style: ContainerStyle, + first_cell: Element<'a, PluginTableMsgIn>, + second_cell: Element<'a, PluginTableMsgIn>, + third_cell: Element<'a, PluginTableMsgIn>, + fourth_cell: Element<'a, PluginTableMsgIn>, + fifth_cell: Element<'a, PluginTableMsgIn>, +) -> Element<'a, PluginTableMsgIn> { + container(row([ + container(first_cell).width(Length::FillPortion(38)).into(), + container(second_cell).width(Length::FillPortion(12)).into(), + container(third_cell).width(Length::FillPortion(24)).into(), + container(fourth_cell).width(Length::FillPortion(15)).into(), + container(fifth_cell).width(Length::FillPortion(10)).into(), + ])) + .class(style) + .into() } -struct Column { - kind: ColumnKind, -} +fn header_cell<'a>(value: &str, first: bool) -> Element<'a, PluginTableMsgIn> { + let mut element = container(text(value.to_string())) + .height(Length::Fixed(30.0)) + .align_y(Alignment::Center); -impl Column { - fn new(kind: ColumnKind) -> Self { - Self { kind } + if first { + element = element.padding(padding::left(8.0)) } + + element.into() } -impl<'a> table::Column<'a, PluginTableMsgIn, GauntletSettingsTheme, Renderer> for Column { - type Row = Row; +fn name_cell<'a>(row_entry: &Row) -> Element<'a, PluginTableMsgIn> { + let toggle = match row_entry { + Row::Plugin { plugin_data, plugin_id } => { + let plugin_data = plugin_data.borrow(); + let plugin_data = plugin_data.plugins_state.get(&plugin_id).unwrap(); - fn header(&'a self, _col_index: usize) -> Element<'a, PluginTableMsgIn> { - match self.kind { - ColumnKind::Name => { - container(text("Name")) - .height(Length::Fixed(30.0)) - .align_y(Alignment::Center) - .padding(padding::left(8.0)) - .into() - } - ColumnKind::Type => { - container(text("Type")) - .height(Length::Fixed(30.0)) - .align_y(Alignment::Center) - .into() - } - ColumnKind::Alias => { - container(text("Alias")) - .height(Length::Fixed(30.0)) - .align_y(Alignment::Center) - .into() - } - ColumnKind::Shortcut => { - container(text("Shortcut")) - .height(Length::Fixed(30.0)) - .align_y(Alignment::Center) - .into() - } - ColumnKind::EnableToggle => { - container(text("Enabled")) - .height(Length::Fixed(30.0)) - .align_y(Alignment::Center) - .into() - } + let icon: Element<_> = if plugin_data.show_entrypoints { + caret_down().into() + } else { + caret_right().into() + }; + + button(icon) + .on_press(PluginTableMsgIn::ToggleShowEntrypoints { + plugin_id: plugin_id.clone(), + }) + .width(Length::Shrink) + .height(Length::Fixed(40.0)) + .padding(8.0) + .class(ButtonStyle::TableRow) + .into() } - } + Row::GeneratedEntrypoint { .. } => horizontal_space().width(Length::Shrink).into(), + Row::Entrypoint { + plugin_data, + plugin_id, + entrypoint_id, + .. + } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); + let plugin_data = plugin_data.plugins_state.get(&plugin_id).unwrap(); - fn cell(&'a self, _col_index: usize, _row_index: usize, row_entry: &'a Self::Row) -> Element<'a, PluginTableMsgIn> { - match self.kind { - ColumnKind::Name => { - let toggle = match row_entry { - Row::Plugin { plugin_data, plugin_id } => { - let plugin_data = plugin_data.borrow(); - let plugin_data = plugin_data.plugins_state.get(&plugin_id).unwrap(); + let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); - let icon = if plugin_data.show_entrypoints { - Bootstrap::CaretDown - } else { - Bootstrap::CaretRight - }; - - let icon: Element<_> = value(icon).font(BOOTSTRAP_FONT).into(); - - button(icon) - .on_press(PluginTableMsgIn::ToggleShowEntrypoints { - plugin_id: plugin_id.clone(), - }) - .width(Length::Shrink) - .height(Length::Fixed(40.0)) - .padding(8.0) - .class(ButtonStyle::TableRow) - .into() - } - Row::GeneratedEntrypoint { .. } => horizontal_space().width(Length::Shrink).into(), - Row::Entrypoint { - plugin_data, - plugin_id, - entrypoint_id, - .. - } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - let plugin_data = plugin_data.plugins_state.get(&plugin_id).unwrap(); - - let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); - - if matches!(entrypoint.entrypoint_type, SettingsEntrypointType::EntrypointGenerator) { - let icon = if plugin_data - .generator_entrypoint_state - .get(&entrypoint_id) - .unwrap() - .show_entrypoints - { - Bootstrap::CaretDown - } else { - Bootstrap::CaretRight - }; - - let icon: Element<_> = value(icon).font(BOOTSTRAP_FONT).into(); - - let content = button(icon) - .on_press(PluginTableMsgIn::ToggleShowGeneratedEntrypoints { - plugin_id: plugin_id.clone(), - entrypoint_id: entrypoint_id.clone(), - }) - .width(Length::Shrink) - .height(Length::Fixed(40.0)) - .padding(8.0) - .class(ButtonStyle::TableRow) - .into(); - - let space: Element<_> = Space::with_width(Length::Fixed(10.0)).into(); - - row(vec![space, content]).into() - } else { - horizontal_space().width(Length::Shrink).into() - } - } + if matches!(entrypoint.entrypoint_type, SettingsEntrypointType::EntrypointGenerator) { + let icon: Element<_> = if plugin_data + .generator_entrypoint_state + .get(&entrypoint_id) + .unwrap() + .show_entrypoints + { + caret_down().into() + } else { + caret_right().into() }; - let content: Element<_> = match row_entry { - Row::Plugin { plugin_data, plugin_id } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - - let plugin_name = text(plugin.plugin_name.to_string()).shaping(Shaping::Advanced).size(14); - - container(plugin_name).align_y(Alignment::Center).into() - } - Row::Entrypoint { - plugin_data, - plugin_id, - entrypoint_id, - .. - } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); - - let text: Element<_> = text(entrypoint.entrypoint_name.to_string()) - .shaping(Shaping::Advanced) - .size(14) - .into(); - - let space: Element<_> = - if let SettingsEntrypointType::EntrypointGenerator = entrypoint.entrypoint_type { - Space::with_width(Length::Fixed(4.0)).into() - } else { - Space::with_width(Length::Fixed(45.0)).into() - }; - - let text: Element<_> = row(vec![space, text]).into(); - - container(text).align_y(Alignment::Center).into() - } - Row::GeneratedEntrypoint { - plugin_data, - plugin_id, - generator_entrypoint_id, - generated_entrypoint_id, - .. - } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - let entrypoint = plugin.entrypoints.get(&generator_entrypoint_id).unwrap(); - let generated_entrypoint = - entrypoint.generated_entrypoints.get(&generated_entrypoint_id).unwrap(); - - let text: Element<_> = text(generated_entrypoint.entrypoint_name.to_string()) - .shaping(Shaping::Advanced) - .size(14) - .into(); - - let space: Element<_> = Space::with_width(Length::Fixed(65.0)).into(); - - let text: Element<_> = row(vec![space, text]).into(); - - container(text).align_y(Alignment::Center).into() - } - }; - - let msg = match &row_entry { - Row::Plugin { plugin_id, .. } => { - SelectedItem::Plugin { - plugin_id: plugin_id.clone(), - } - } - Row::Entrypoint { - entrypoint_id, - plugin_id, - .. - } => { - SelectedItem::Entrypoint { - plugin_id: plugin_id.clone(), - entrypoint_id: entrypoint_id.clone(), - } - } - Row::GeneratedEntrypoint { - plugin_id, - generator_entrypoint_id, - generated_entrypoint_id, - .. - } => { - SelectedItem::GeneratedEntrypoint { - plugin_id: plugin_id.clone(), - generator_entrypoint_id: generator_entrypoint_id.clone(), - generated_entrypoint_id: generated_entrypoint_id.clone(), - } - } - }; - - let content = button(content) - .class(ButtonStyle::TableRow) - .on_press(PluginTableMsgIn::SelectItem(msg)) - .width(Length::Fill) - .height(Length::Fixed(40.0)) - .padding(padding::all(8).left(0)) - .into(); - - row(vec![toggle, content]).into() - } - ColumnKind::Type => { - let content: Element<_> = match row_entry { - Row::Plugin { .. } => container(text("Plugin").size(14)).align_y(Alignment::Center).into(), - Row::Entrypoint { - plugin_data, - plugin_id, - entrypoint_id, - .. - } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); - - let entrypoint_type = match entrypoint.entrypoint_type { - SettingsEntrypointType::Command => "Command", - SettingsEntrypointType::View => "View", - SettingsEntrypointType::InlineView => "Inline", - SettingsEntrypointType::EntrypointGenerator => "Generator", - }; - - container(text(entrypoint_type.to_string()).size(14)) - .align_y(Alignment::Center) - .into() - } - Row::GeneratedEntrypoint { .. } => { - container(text("Generated").size(14)).align_y(Alignment::Center).into() - } - }; - - let msg = match &row_entry { - Row::Plugin { plugin_id, .. } => { - SelectedItem::Plugin { - plugin_id: plugin_id.clone(), - } - } - Row::Entrypoint { - entrypoint_id, - plugin_id, - .. - } => { - SelectedItem::Entrypoint { - plugin_id: plugin_id.clone(), - entrypoint_id: entrypoint_id.clone(), - } - } - Row::GeneratedEntrypoint { - plugin_id, - generated_entrypoint_id, - generator_entrypoint_id, - .. - } => { - SelectedItem::GeneratedEntrypoint { - plugin_id: plugin_id.clone(), - generator_entrypoint_id: generator_entrypoint_id.clone(), - generated_entrypoint_id: generated_entrypoint_id.clone(), - } - } - }; - - button(content) - .class(ButtonStyle::TableRow) - .on_press(PluginTableMsgIn::SelectItem(msg)) - .width(Length::Fill) + let content = button(icon) + .on_press(PluginTableMsgIn::ToggleShowGeneratedEntrypoints { + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + }) + .width(Length::Shrink) .height(Length::Fixed(40.0)) .padding(8.0) - .into() + .class(ButtonStyle::TableRow) + .into(); + + let space: Element<_> = Space::with_width(Length::Fixed(10.0)).into(); + + row(vec![space, content]).into() + } else { + horizontal_space().width(Length::Shrink).into() } - ColumnKind::Shortcut => { - match row_entry { - Row::Plugin { .. } => horizontal_space().into(), - Row::Entrypoint { - plugin_data, - plugin_id, - entrypoint_id, - shortcut_data, - .. - } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); + } + }; - if let SettingsEntrypointType::View | SettingsEntrypointType::Command = - entrypoint.entrypoint_type - { - let shortcut_selector = shortcut_selector( - shortcut_data, - move |shortcut| { - PluginTableMsgIn::ShortcutCaptured( - plugin_id.clone(), - entrypoint_id.clone(), - shortcut, - ) - }, - ContainerStyle::Box, - true, - ); + let content: Element<_> = match row_entry { + Row::Plugin { plugin_data, plugin_id } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - container(shortcut_selector) - .height(Length::Fixed(40.0)) - .width(Length::Fill) - .into() - } else { - horizontal_space().into() + let plugin_name = text(plugin.plugin_name.to_string()).shaping(Shaping::Advanced).size(14); + + container(plugin_name).align_y(Alignment::Center).into() + } + Row::Entrypoint { + plugin_data, + plugin_id, + entrypoint_id, + .. + } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); + let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); + + let text: Element<_> = text(entrypoint.entrypoint_name.to_string()) + .shaping(Shaping::Advanced) + .size(14) + .into(); + + let space: Element<_> = if let SettingsEntrypointType::EntrypointGenerator = entrypoint.entrypoint_type { + Space::with_width(Length::Fixed(4.0)).into() + } else { + Space::with_width(Length::Fixed(45.0)).into() + }; + + let text: Element<_> = row(vec![space, text]).into(); + + container(text).align_y(Alignment::Center).into() + } + Row::GeneratedEntrypoint { + plugin_data, + plugin_id, + generator_entrypoint_id, + generated_entrypoint_id, + .. + } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); + let entrypoint = plugin.entrypoints.get(&generator_entrypoint_id).unwrap(); + let generated_entrypoint = entrypoint.generated_entrypoints.get(&generated_entrypoint_id).unwrap(); + + let text: Element<_> = text(generated_entrypoint.entrypoint_name.to_string()) + .shaping(Shaping::Advanced) + .size(14) + .into(); + + let space: Element<_> = Space::with_width(Length::Fixed(65.0)).into(); + + let text: Element<_> = row(vec![space, text]).into(); + + container(text).align_y(Alignment::Center).into() + } + }; + + let msg = match row_entry { + Row::Plugin { plugin_id, .. } => { + SelectedItem::Plugin { + plugin_id: plugin_id.clone(), + } + } + Row::Entrypoint { + entrypoint_id, + plugin_id, + .. + } => { + SelectedItem::Entrypoint { + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + } + } + Row::GeneratedEntrypoint { + plugin_id, + generator_entrypoint_id, + generated_entrypoint_id, + .. + } => { + SelectedItem::GeneratedEntrypoint { + plugin_id: plugin_id.clone(), + generator_entrypoint_id: generator_entrypoint_id.clone(), + generated_entrypoint_id: generated_entrypoint_id.clone(), + } + } + }; + + let content = button(content) + .class(ButtonStyle::TableRow) + .on_press(PluginTableMsgIn::SelectItem(msg)) + .width(Length::Fill) + .height(Length::Fixed(40.0)) + .padding(padding::all(8).left(0)) + .into(); + + row(vec![toggle, content]).into() +} + +fn type_cell<'a>(row_entry: &Row) -> Element<'a, PluginTableMsgIn> { + let content: Element<_> = match row_entry { + Row::Plugin { .. } => container(text("Plugin").size(14)).align_y(Alignment::Center).into(), + Row::Entrypoint { + plugin_data, + plugin_id, + entrypoint_id, + .. + } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); + let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); + + let entrypoint_type = match entrypoint.entrypoint_type { + SettingsEntrypointType::Command => "Command", + SettingsEntrypointType::View => "View", + SettingsEntrypointType::InlineView => "Inline", + SettingsEntrypointType::EntrypointGenerator => "Generator", + }; + + container(text(entrypoint_type.to_string()).size(14)) + .align_y(Alignment::Center) + .into() + } + Row::GeneratedEntrypoint { .. } => container(text("Generated").size(14)).align_y(Alignment::Center).into(), + }; + + let msg = match row_entry { + Row::Plugin { plugin_id, .. } => { + SelectedItem::Plugin { + plugin_id: plugin_id.clone(), + } + } + Row::Entrypoint { + entrypoint_id, + plugin_id, + .. + } => { + SelectedItem::Entrypoint { + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + } + } + Row::GeneratedEntrypoint { + plugin_id, + generated_entrypoint_id, + generator_entrypoint_id, + .. + } => { + SelectedItem::GeneratedEntrypoint { + plugin_id: plugin_id.clone(), + generator_entrypoint_id: generator_entrypoint_id.clone(), + generated_entrypoint_id: generated_entrypoint_id.clone(), + } + } + }; + + button(content) + .class(ButtonStyle::TableRow) + .on_press(PluginTableMsgIn::SelectItem(msg)) + .width(Length::Fill) + .height(Length::Fixed(40.0)) + .padding(8.0) + .into() +} + +fn shortcut_cell<'a>(row_entry: &Row) -> Element<'a, PluginTableMsgIn> { + match row_entry { + Row::Plugin { .. } => horizontal_space().into(), + Row::Entrypoint { + plugin_data, + plugin_id, + entrypoint_id, + shortcut_data, + .. + } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); + let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); + + if let SettingsEntrypointType::View | SettingsEntrypointType::Command = entrypoint.entrypoint_type { + let shortcut_selector = shortcut_selector( + shortcut_data, + { + let plugin_id = plugin_id.clone(); + let entrypoint_id = entrypoint_id.clone(); + + move |shortcut| { + PluginTableMsgIn::ShortcutCaptured(plugin_id.clone(), entrypoint_id.clone(), shortcut) } - } - Row::GeneratedEntrypoint { - plugin_id, - generated_entrypoint_id, - shortcut_data, - .. - } => { - let shortcut_selector = shortcut_selector( - shortcut_data, - move |shortcut| { - PluginTableMsgIn::ShortcutCaptured( - plugin_id.clone(), - generated_entrypoint_id.clone(), - shortcut, - ) - }, - ContainerStyle::Box, - true, - ); + }, + ContainerStyle::Box, + true, + ); - container(shortcut_selector) - .height(Length::Fixed(40.0)) - .width(Length::Fill) - .into() - } - } - } - ColumnKind::Alias => { - match row_entry { - Row::Plugin { .. } => horizontal_space().into(), - Row::Entrypoint { - plugin_data, - plugin_id, - entrypoint_id, - search_alias, - .. - } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); - - if let SettingsEntrypointType::View | SettingsEntrypointType::Command = - entrypoint.entrypoint_type - { - let input = text_input("Add Alias", search_alias.as_deref().unwrap_or("")) - .class(TextInputStyle::EntrypointAlias) - .padding(padding::all(12.0).left(7.0)) - .size(14) - .on_input(move |alias| { - PluginTableMsgIn::AliasChanged(plugin_id.clone(), entrypoint_id.clone(), alias) - }); - - container(input).height(Length::Fixed(40.0)).width(Length::Fill).into() - } else { - horizontal_space().into() - } - } - Row::GeneratedEntrypoint { - plugin_id, - generated_entrypoint_id, - search_alias, - .. - } => { - let input = text_input("Add Alias", search_alias.as_deref().unwrap_or("")) - .class(TextInputStyle::EntrypointAlias) - .padding(padding::all(12.0).left(7.0)) - .size(14) - .on_input(move |alias| { - PluginTableMsgIn::AliasChanged( - plugin_id.clone(), - generated_entrypoint_id.clone(), - alias, - ) - }); - - container(input).height(Length::Fixed(40.0)).width(Length::Fill).into() - } - } - } - ColumnKind::EnableToggle => { - let (enabled, show_checkbox, plugin_id, entrypoint_id) = match &row_entry { - Row::Plugin { plugin_data, plugin_id } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - - (plugin.enabled, true, plugin.plugin_id.clone(), None) - } - Row::Entrypoint { - plugin_data, - entrypoint_id, - plugin_id, - .. - } => { - let plugin_data = plugin_data.borrow(); - let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); - let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); - - ( - entrypoint.enabled, - plugin.enabled, - plugin.plugin_id.clone(), - Some(entrypoint.entrypoint_id.clone()), - ) - } - Row::GeneratedEntrypoint { .. } => return horizontal_space().into(), - }; - - let on_toggle = if show_checkbox { - Some(move |enabled| { - let enabled_item = match &entrypoint_id { - None => { - EnabledItem::Plugin { - enabled, - plugin_id: plugin_id.clone(), - } - } - Some(entrypoint_id) => { - EnabledItem::Entrypoint { - enabled, - plugin_id: plugin_id.clone(), - entrypoint_id: entrypoint_id.clone(), - } - } - }; - PluginTableMsgIn::EnabledToggleItem(enabled_item) - }) - } else { - None - }; - - let checkbox: Element<_> = checkbox("", enabled).on_toggle_maybe(on_toggle).into(); - - container(checkbox) - .width(Length::Fill) + container(shortcut_selector) .height(Length::Fixed(40.0)) - .align_y(Alignment::Center) - .align_x(Alignment::Center) + .width(Length::Fill) .into() + } else { + horizontal_space().into() } } - } + Row::GeneratedEntrypoint { + plugin_id, + generated_entrypoint_id, + shortcut_data, + .. + } => { + let shortcut_selector = shortcut_selector( + shortcut_data, + { + let plugin_id = plugin_id.clone(); + let generated_entrypoint_id = generated_entrypoint_id.clone(); - fn width(&self) -> f32 { - match self.kind { - ColumnKind::Name => 300.0, - ColumnKind::Type => 100.0, - ColumnKind::Shortcut => 190.0, - ColumnKind::Alias => 120.0, - ColumnKind::EnableToggle => 75.0, + move |shortcut| { + PluginTableMsgIn::ShortcutCaptured(plugin_id.clone(), generated_entrypoint_id.clone(), shortcut) + } + }, + ContainerStyle::Box, + true, + ); + + container(shortcut_selector) + .height(Length::Fixed(40.0)) + .width(Length::Fill) + .into() } } - - fn resize_offset(&self) -> Option { - None - } +} + +fn alias_cell<'a>(row_entry: &Row) -> Element<'a, PluginTableMsgIn> { + match &row_entry { + Row::Plugin { .. } => horizontal_space().into(), + Row::Entrypoint { + plugin_data, + plugin_id, + entrypoint_id, + search_alias, + .. + } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); + let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); + + if let SettingsEntrypointType::View | SettingsEntrypointType::Command = entrypoint.entrypoint_type { + let input = text_input("Add Alias", search_alias.as_deref().unwrap_or("")) + .class(TextInputStyle::EntrypointAlias) + .padding(padding::all(12.0).left(7.0)) + .size(14) + .on_input({ + let plugin_id = plugin_id.clone(); + let entrypoint_id = entrypoint_id.clone(); + + move |alias| PluginTableMsgIn::AliasChanged(plugin_id.clone(), entrypoint_id.clone(), alias) + }); + + container(input).height(Length::Fixed(40.0)).width(Length::Fill).into() + } else { + horizontal_space().into() + } + } + Row::GeneratedEntrypoint { + plugin_id, + generated_entrypoint_id, + search_alias, + .. + } => { + let input = text_input("Add Alias", search_alias.as_deref().unwrap_or("")) + .class(TextInputStyle::EntrypointAlias) + .padding(padding::all(12.0).left(7.0)) + .size(14) + .on_input({ + let plugin_id = plugin_id.clone(); + let generated_entrypoint_id = generated_entrypoint_id.clone(); + + move |alias| { + PluginTableMsgIn::AliasChanged(plugin_id.clone(), generated_entrypoint_id.clone(), alias) + } + }); + + container(input).height(Length::Fixed(40.0)).width(Length::Fill).into() + } + } +} + +fn enable_cell<'a>(row: &Row) -> Element<'a, PluginTableMsgIn> { + let (enabled, show_checkbox, plugin_id, entrypoint_id) = match row { + Row::Plugin { plugin_data, plugin_id } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); + + (plugin.enabled, true, plugin.plugin_id.clone(), None) + } + Row::Entrypoint { + plugin_data, + entrypoint_id, + plugin_id, + .. + } => { + let plugin_data = plugin_data.borrow(); + let plugin = plugin_data.plugins.get(&plugin_id).unwrap(); + let entrypoint = plugin.entrypoints.get(&entrypoint_id).unwrap(); + + ( + entrypoint.enabled, + plugin.enabled, + plugin.plugin_id.clone(), + Some(entrypoint.entrypoint_id.clone()), + ) + } + Row::GeneratedEntrypoint { .. } => return horizontal_space().into(), + }; + + let on_toggle = if show_checkbox { + Some(move |enabled| { + let enabled_item = match &entrypoint_id { + None => { + EnabledItem::Plugin { + enabled, + plugin_id: plugin_id.clone(), + } + } + Some(entrypoint_id) => { + EnabledItem::Entrypoint { + enabled, + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + } + } + }; + PluginTableMsgIn::EnabledToggleItem(enabled_item) + }) + } else { + None + }; + + let checkbox: Element<_> = checkbox("", enabled).on_toggle_maybe(on_toggle).into(); + + container(checkbox) + .width(Length::Fill) + .height(Length::Fixed(40.0)) + .align_y(Alignment::Center) + .align_x(Alignment::Center) + .into() } diff --git a/rust/plugin_runtime/src/lib.rs b/rust/plugin_runtime/src/lib.rs index 7d44b5c..a228c29 100644 --- a/rust/plugin_runtime/src/lib.rs +++ b/rust/plugin_runtime/src/lib.rs @@ -16,6 +16,7 @@ mod ui; use std::ops::Deref; +use anyhow::Context; use anyhow::anyhow; use gauntlet_common_plugin_runtime::JsMessageSide; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; @@ -59,6 +60,8 @@ pub fn run_plugin_runtime(socket_name: String) { async fn run_outer(socket_name: String) -> anyhow::Result<()> { tracing::info!("Starting plugin runtime at socket: {}", &socket_name); + let debug_socket_name = socket_name.clone(); + let stop_token = CancellationToken::new(); #[cfg(target_os = "windows")] @@ -67,7 +70,7 @@ async fn run_outer(socket_name: String) -> anyhow::Result<()> { #[cfg(unix)] let name = socket_name.to_fs_name::()?; - let conn = Stream::connect(name).await?; + let conn = Stream::connect(name).await.context(debug_socket_name)?; let (mut recver, mut sender) = conn.split(); diff --git a/rust/plugin_runtime/src/ui.rs b/rust/plugin_runtime/src/ui.rs index 43a2165..a92fd42 100644 --- a/rust/plugin_runtime/src/ui.rs +++ b/rust/plugin_runtime/src/ui.rs @@ -115,7 +115,8 @@ pub fn op_react_replace_view<'a>( let mut deserializer = serde_v8::Deserializer::new(scope, container.v8_value, None); - let container = RootWidget::deserialize(&mut deserializer).map_err(|err| anyhow!(err))?; + let container = RootWidget::deserialize(&mut deserializer) + .map_err(|err| anyhow!("Unable to deserialize root widget: {:#}", err))?; let entrypoint_id = EntrypointId::from_string(entrypoint_id); let entrypoint_name = entrypoint_name.to_string(); diff --git a/rust/scenario_runner/src/lib.rs b/rust/scenario_runner/src/lib.rs index 2336599..0eec05a 100644 --- a/rust/scenario_runner/src/lib.rs +++ b/rust/scenario_runner/src/lib.rs @@ -30,8 +30,6 @@ pub async fn run_scenario_runner_mock_server( data: UiSetupData { window_position_file: None, theme, - global_shortcut: None, - global_entrypoint_shortcuts: Default::default(), close_on_unfocus: false, window_position_mode: WindowPositionMode::Static, }, diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index 441538b..c34d73a 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -7,7 +7,6 @@ edition.workspace = true gauntlet-common.workspace = true gauntlet-utils.workspace = true gauntlet-utils-macros.workspace = true -gauntlet-client.workspace = true gauntlet-common-plugin-runtime.workspace = true gauntlet-plugin-runtime.workspace = true gauntlet-scenario-runner = { workspace = true, optional = true } @@ -32,6 +31,7 @@ interprocess.workspace = true toml.workspace = true # other +global-hotkey = "0.7.0" tantivy = "0.22" git2 = { version = "0.19", features = ["vendored-libgit2", "vendored-openssl"] } tempfile = "3" @@ -43,7 +43,6 @@ uuid = "1.8" arboard = { version = "3.4", features = ["wayland-data-control"] } url = "2.5" ureq = "2.10" -vergen-pretty = "0.3" dark-light = "1.1.1" schemars = "0.8" @@ -51,5 +50,3 @@ schemars = "0.8" release = ["gauntlet-common/release", "gauntlet-plugin-runtime/release"] scenario_runner = ["dep:gauntlet-scenario-runner", "gauntlet-common/scenario_runner", "gauntlet-plugin-runtime/scenario_runner"] -[build-dependencies] -vergen-gitcl = { version = "1.0", features = ["build", "cargo"] } diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 1f9e4d7..91bd26e 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -1,116 +1,10 @@ -use std::backtrace::Backtrace; -use std::fs::File; -use std::io::Write; -use std::process::exit; -use std::sync::Arc; -use std::time::SystemTime; -use std::time::UNIX_EPOCH; - -use gauntlet_client::start_client; -use gauntlet_common::dirs::Dirs; -use gauntlet_common::model::EntrypointId; -use gauntlet_common::model::PluginId; -use gauntlet_common::rpc::backend_api::BackendForCliApi; -use gauntlet_common::rpc::backend_api::BackendForCliApiProxy; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiRequestData; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiResponseData; -use gauntlet_common::rpc::backend_api::GrpcBackendApi; -use gauntlet_common::rpc::backend_api::handle_proxy_message; -use gauntlet_common::rpc::backend_server::start_backend_server; -use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; -use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; -use gauntlet_utils::channel::RequestError; -use gauntlet_utils::channel::RequestReceiver; -use gauntlet_utils::channel::RequestSender; -use gauntlet_utils::channel::channel; -use vergen_pretty::vergen_pretty_env; - -use crate::plugins::ApplicationManager; -use crate::rpc::BackendServerImpl; - -pub(crate) mod model; +mod model; pub mod plugins; -pub mod rpc; -pub(crate) mod search; +mod rpc; +mod search; -const PLUGIN_CONNECT_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_CONNECT__"; -const PLUGIN_UUID_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_UUID__"; - -pub fn start(#[cfg(not(feature = "scenario_runner"))] minimized: bool) { - register_panic_hook(std::env::var(PLUGIN_UUID_ENV).ok()); - - if let Ok(socket_name) = std::env::var(PLUGIN_CONNECT_ENV) { - gauntlet_plugin_runtime::run_plugin_runtime(socket_name); - - return; - } - - tracing::info!("Gauntlet Build Information:"); - for (name, value) in vergen_pretty_env!() { - if let Some(value) = value { - tracing::info!("{}: {}", name, value); - } - } - - #[cfg(feature = "scenario_runner")] - run_scenario_runner(); - - #[cfg(not(feature = "scenario_runner"))] - { - if is_server_running() { - gauntlet_client::open_window() - } else { - let (frontend_sender, frontend_receiver) = channel::(); - let (backend_sender, backend_receiver) = - channel::(); - - std::thread::Builder::new() - .name("gauntlet-server".to_string()) - .spawn(|| { - start_server(frontend_sender, backend_receiver); - }) - .expect("failed to spawn thread"); - - start_client(minimized, frontend_receiver, backend_sender) - } - } -} - -pub fn run_action(plugin_id: String, entrypoint_id: String, action_id: String) { - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .expect("unable to start server tokio runtime") - .block_on(async { - let result = GrpcBackendApi::new().await; - - match result { - Ok(backend_api) => { - let backend_api = BackendForCliApiProxy::new(backend_api); - - let plugin_id = PluginId::from_string(plugin_id); - let entrypoint_id = EntrypointId::from_string(entrypoint_id); - - if let Err(err) = backend_api.run_action(plugin_id, entrypoint_id, action_id).await { - match err { - RequestError::Timeout => { - tracing::error!("Timeout occurred when handling command"); - } - RequestError::Other { display: value } => { - tracing::error!("Error occurred when handling command: {}", value); - } - RequestError::OtherSideWasDropped => { - tracing::error!("Error occurred when handling command: Other side was dropped"); - } - } - } - } - Err(_) => { - tracing::error!("Unable to connect to server. Please check if you have Gauntlet running on your PC") - } - } - }) -} +pub const PLUGIN_CONNECT_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_CONNECT__"; +pub const PLUGIN_UUID_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_UUID__"; #[cfg(feature = "scenario_runner")] fn run_scenario_runner() { @@ -129,7 +23,7 @@ fn run_scenario_runner() { start_mock_server(frontend_sender, backend_receiver, theme.macos_dark_theme) }); - start_client(false, frontend_receiver, backend_sender); + start_app(false, frontend_receiver, backend_sender); } "scenario_runner" => { let (frontend_sender, frontend_receiver) = channel::(); @@ -144,41 +38,6 @@ fn run_scenario_runner() { } } -#[cfg(not(feature = "scenario_runner"))] -fn is_server_running() -> bool { - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .expect("unable to start server tokio runtime") - .block_on(async { - let test_fn = || { - async { - let api = GrpcBackendApi::new().await?; - - let api = BackendForCliApiProxy::new(api); - - api.ping().await?; - - anyhow::Ok(()) - } - }; - - test_fn().await.is_ok() - }) -} - -fn start_server( - request_sender: RequestSender, - backend_receiver: RequestReceiver, -) { - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .expect("unable to start server tokio runtime") - .block_on(async { run_server(request_sender, backend_receiver).await }) - .unwrap(); -} - #[cfg(feature = "scenario_runner")] fn start_mock_server( request_sender: RequestSender, @@ -209,104 +68,3 @@ fn start_frontend_mock( }) .unwrap(); } - -async fn run_server( - frontend_sender: RequestSender, - mut backend_receiver: RequestReceiver, -) -> anyhow::Result<()> { - let application_manager = ApplicationManager::create(frontend_sender).await?; - - let application_manager = Arc::new(application_manager); - - application_manager.clear_all_icon_cache_dir()?; - - #[cfg(not(feature = "scenario_runner"))] - if let Err(err) = application_manager.load_bundled_plugins().await { - tracing::error!("error loading bundled plugin(s): {:?}", err); - } - - #[cfg(not(any(feature = "scenario_runner", feature = "release")))] - { - let plugin_path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../dev_plugin").to_owned(); - let plugin_path = std::fs::canonicalize(plugin_path).expect("valid path"); - let plugin_path = plugin_path.to_str().expect("valid utf8"); - - if let Err(err) = application_manager.save_local_plugin(plugin_path).await { - tracing::error!("error loading dev plugin: {:?}", err); - } - } - - application_manager.reload_all_plugins().await?; - - tokio::spawn({ - let application_manager = application_manager.clone(); - - async move { - start_backend_server( - Box::new(BackendServerImpl::new(application_manager.clone())), - Box::new(BackendServerImpl::new(application_manager.clone())), - Box::new(BackendServerImpl::new(application_manager.clone())), - ) - .await - } - }); - - loop { - let (request_data, responder) = backend_receiver.recv().await; - - let response_data = handle_proxy_message(request_data, application_manager.as_ref()).await; - - responder.respond(response_data); - } -} - -fn register_panic_hook(plugin_runtime: Option) { - unsafe { - std::env::set_var("RUST_BACKTRACE", "full"); - }; - - let dirs = Dirs::new(); - - let crash_file = match plugin_runtime { - None => dirs.server_crash_log_file(), - Some(plugin_uuid) => dirs.plugin_crash_log_file(&plugin_uuid), - }; - - let _ = std::fs::remove_file(&crash_file); - - std::panic::set_hook(Box::new(move |panic_info| { - let payload = panic_info.payload(); - - let payload = if let Some(&s) = payload.downcast_ref::<&'static str>() { - s - } else if let Some(s) = payload.downcast_ref::() { - s.as_str() - } else { - "Box" - }; - - let location = panic_info.location().map(|l| l.to_string()); - let backtrace = Backtrace::capture(); - - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .ok() - .map(|duration| duration.as_millis().to_string()) - .unwrap_or("Unknown".to_string()); - - let content = format!( - "Panic on {}\nPayload: {}\nLocation: {:?}\nBacktrace:\n{}", - now, payload, location, backtrace - ); - - let crash_file = File::options().create(true).append(true).open(&crash_file); - - if let Ok(mut crash_file) = crash_file { - let _ = crash_file.write_all(content.as_bytes()); - } - - eprintln!("{}", content); - - exit(101); // poor man's abort on panic because actual setting makes v8 linking fail - })); -} diff --git a/rust/server/src/plugins/config_reader.rs b/rust/server/src/plugins/config_reader.rs index 8fd48f5..0f416df 100644 --- a/rust/server/src/plugins/config_reader.rs +++ b/rust/server/src/plugins/config_reader.rs @@ -17,7 +17,7 @@ impl ConfigReader { } } - pub async fn reload_config(&self) -> anyhow::Result<()> { + pub fn reload_config(&self) -> anyhow::Result<()> { let config = self.read_config(); self.close_on_unfocus.store( diff --git a/rust/server/src/plugins/data_db_repository.rs b/rust/server/src/plugins/data_db_repository.rs index 2aee1f7..fa4f712 100644 --- a/rust/server/src/plugins/data_db_repository.rs +++ b/rust/server/src/plugins/data_db_repository.rs @@ -381,7 +381,7 @@ pub struct DbPluginEntrypointFrecencyStats { const SETTINGS_DATA_ID: &str = "settings_data"; // only one row in the table impl DataDbRepository { - pub async fn new(dirs: Dirs) -> anyhow::Result { + pub fn new(dirs: Dirs) -> anyhow::Result { let data_db_file = dirs.data_db_file()?; std::fs::create_dir_all(&data_db_file.parent().unwrap()).context("Unable to create data directory")?; @@ -1053,7 +1053,7 @@ impl DataDbRepository { Ok(theme) } - pub fn set_settings(&self, value: DbSettings) -> anyhow::Result<()> { + fn set_settings(&self, value: &DbSettings) -> anyhow::Result<()> { let connection = self.connection.lock().map_err(|_| anyhow!("lock is poisoned"))?; // language=SQLite @@ -1075,6 +1075,19 @@ impl DataDbRepository { Ok(()) } + pub fn mutate_settings( + &self, + mutate: impl FnOnce(DbSettings) -> anyhow::Result, + ) -> anyhow::Result { + let settings = self.get_settings()?; + + let settings = mutate(settings)?; + + self.set_settings(&settings)?; + + Ok(settings) + } + pub fn set_preference_value( &self, plugin_id: String, diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index f90a249..c5e3970 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -232,6 +232,8 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run uds_socket_file.to_fs_name::()? }; + tracing::info!("Starting plugin {:?} - {:?}", &plugin_id, &data.uuid); + let listener = ListenerOptions::new().name(name).reclaim_name(false).create_tokio()?; let home_dir = home_dir diff --git a/rust/server/src/plugins/loader.rs b/rust/server/src/plugins/loader.rs index 23547a5..c5366da 100644 --- a/rust/server/src/plugins/loader.rs +++ b/rust/server/src/plugins/loader.rs @@ -56,7 +56,7 @@ impl PluginLoader { self.download_status_holder.download_status() } - pub async fn download_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { + pub fn download_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { let download_status_guard = self.download_status_holder.download_started(plugin_id.clone()); let data_db_repository = self.db_repository.clone(); @@ -72,7 +72,7 @@ impl PluginLoader { PluginLoader::download(temp_dir.path(), plugin_id_clone.clone())?; - let plugin_data = PluginLoader::read_plugin_dir(temp_dir.path(), plugin_id_clone.clone()).await?; + let plugin_data = PluginLoader::read_plugin_dir(temp_dir.path(), plugin_id_clone.clone())?; data_db_repository.save_plugin(DbWritePlugin { id: plugin_data.id, @@ -108,13 +108,12 @@ impl PluginLoader { Ok(()) } - pub async fn save_local_plugin(&self, path: &str) -> anyhow::Result { + pub fn save_local_plugin(&self, path: &str) -> anyhow::Result { let plugin_id = PluginId::from_string(format!("file://{}", &path)); let plugin_dir = plugin_id.try_to_path()?.join("dist"); let plugin_data = PluginLoader::read_plugin_dir(&plugin_dir, plugin_id.clone()) - .await .context(format!("Unable to read plugin: {}", &plugin_id.to_string()))?; self.db_repository.save_plugin(DbWritePlugin { @@ -133,14 +132,13 @@ impl PluginLoader { Ok(plugin_id) } - pub async fn save_bundled_plugin(&self, id: &str, dir: &Dir<'_>) -> anyhow::Result { + pub fn save_bundled_plugin(&self, id: &str, dir: &Dir<'_>) -> anyhow::Result { let plugin_id = PluginId::from_string(format!("bundled://{id}")); let temp_dir = tempfile::tempdir()?; dir.extract(&temp_dir)?; let plugin_data = PluginLoader::read_plugin_dir(temp_dir.path(), plugin_id.clone()) - .await .context(format!("Unable to read plugin: {}", &plugin_id.to_string()))?; self.db_repository.save_plugin(DbWritePlugin { @@ -169,7 +167,7 @@ impl PluginLoader { Ok(()) } - async fn read_plugin_dir(plugin_dir: &Path, plugin_id: PluginId) -> anyhow::Result { + fn read_plugin_dir(plugin_dir: &Path, plugin_id: PluginId) -> anyhow::Result { let js_dir = plugin_dir.join("js"); let assets = plugin_dir.join("assets"); diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 9ef5b5d..1b03137 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::sync::Arc; use anyhow::anyhow; use gauntlet_common::SETTINGS_ENV; @@ -27,6 +28,10 @@ use gauntlet_common::model::UiSetupData; use gauntlet_common::model::UiWidgetId; use gauntlet_common::model::WindowPositionMode; use gauntlet_common::rpc::backend_api::BackendForFrontendApi; +use gauntlet_common::rpc::backend_api::BackendForFrontendApiRequestData; +use gauntlet_common::rpc::backend_api::BackendForFrontendApiResponseData; +use gauntlet_common::rpc::backend_api::handle_proxy_message; +use gauntlet_common::rpc::backend_server::start_backend_server; use gauntlet_common::rpc::frontend_api::FrontendApi; use gauntlet_common::rpc::frontend_api::FrontendApiProxy; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; @@ -36,6 +41,7 @@ use gauntlet_common_plugin_runtime::model::JsPluginCode; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsExec; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsFileSystem; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsMainSearchBar; +use gauntlet_utils::channel::RequestReceiver; use gauntlet_utils::channel::RequestResult; use gauntlet_utils::channel::RequestSender; use include_dir::Dir; @@ -61,7 +67,10 @@ use crate::plugins::js::PluginRuntimeData; use crate::plugins::js::start_plugin_runtime; use crate::plugins::loader::PluginLoader; use crate::plugins::run_status::RunStatusHolder; -use crate::plugins::settings::Settings; +pub(crate) use crate::plugins::settings::Settings; +use crate::plugins::settings::global_shortcut::GlobalShortcutAction; +use crate::plugins::settings::global_shortcut::GlobalShortcutPressedEvent; +use crate::rpc::BackendServerImpl; use crate::search::EntrypointActionDataView; use crate::search::EntrypointActionType; use crate::search::EntrypointDataView; @@ -102,12 +111,12 @@ pub struct ApplicationManager { } impl ApplicationManager { - pub async fn create( + pub fn create( frontend_sender: RequestSender, ) -> anyhow::Result { let frontend_api = FrontendApiProxy::new(frontend_sender); let dirs = Dirs::new(); - let db_repository = DataDbRepository::new(dirs.clone()).await?; + let db_repository = DataDbRepository::new(dirs.clone())?; let plugin_downloader = PluginLoader::new(db_repository.clone()); let config_reader = ConfigReader::new(dirs.clone()); let icon_cache = IconCache::new(dirs.clone()); @@ -118,7 +127,9 @@ impl ApplicationManager { let (command_broadcaster, _) = tokio::sync::broadcast::channel::(100); - Ok(Self { + icon_cache.clear_all_icon_cache_dir()?; + + let application_manager = Self { config_reader, search_index, command_broadcaster, @@ -130,57 +141,63 @@ impl ApplicationManager { clipboard, settings, dirs, - }) + }; + + #[cfg(not(feature = "scenario_runner"))] + if let Err(err) = application_manager.load_bundled_plugins() { + tracing::error!("error loading bundled plugin(s): {:?}", err); + } + + #[cfg(not(any(feature = "scenario_runner", feature = "release")))] + if let Err(err) = application_manager.save_local_dev_plugin() { + tracing::error!("error loading dev plugin: {:?}", err); + } + + application_manager.reload_all_plugins()?; + + Ok(application_manager) } - pub async fn setup_data(&self) -> anyhow::Result { + pub async fn run_grpc_server(self: &Arc) { + start_backend_server( + Box::new(BackendServerImpl::new(self.clone())), + Box::new(BackendServerImpl::new(self.clone())), + Box::new(BackendServerImpl::new(self.clone())), + ) + .await + } + + pub async fn run_message_loop( + self: &Arc, + mut backend_receiver: RequestReceiver, + ) { + loop { + let (request_data, responder) = backend_receiver.recv().await; + + let response_data = handle_proxy_message(request_data, self.as_ref()).await; + + responder.respond(response_data); + } + } + + pub fn setup(&self) -> anyhow::Result { + self.settings.setup()?; + let window_position_file = self.dirs.window_position(); - let theme = self.settings.effective_theme().await?; - let global_shortcut = self.settings.global_shortcut().await?.map(|(shortcut, _)| shortcut); - let global_entrypoint_shortcuts = self - .settings - .global_entrypoint_shortcuts() - .await? - .into_iter() - .map(|((plugin_id, entrypoint_id), (shortcut, _))| ((plugin_id, entrypoint_id), shortcut)) - .collect(); - let window_position_mode = self.settings.window_position_mode_setting().await?; + let theme = self.settings.effective_theme()?; + let window_position_mode = self.settings.window_position_mode_setting()?; let close_on_unfocus = self.config_reader.close_on_unfocus(); Ok(UiSetupData { window_position_file: Some(window_position_file), theme, - global_shortcut, - global_entrypoint_shortcuts, close_on_unfocus, window_position_mode, }) } - pub async fn setup_response( - &self, - global_shortcut_error: Option, - global_entrypoint_shortcuts_errors: HashMap<(PluginId, EntrypointId), Option>, - ) -> anyhow::Result<()> { - self.settings.set_global_shortcut_error(global_shortcut_error).await?; - - for ((plugin_id, entrypoint_id), error) in global_entrypoint_shortcuts_errors { - self.settings - .set_global_entrypoint_shortcut_error(plugin_id, entrypoint_id, error) - .await?; - } - - Ok(()) - } - - pub fn clear_all_icon_cache_dir(&self) -> anyhow::Result<()> { - tracing::debug!("clearing all icon cache"); - - self.icon_cache.clear_all_icon_cache_dir() - } - - pub async fn download_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { - self.plugin_downloader.download_plugin(plugin_id).await + pub fn download_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { + self.plugin_downloader.download_plugin(plugin_id) } pub fn download_status(&self) -> HashMap { @@ -333,14 +350,14 @@ impl ApplicationManager { Ok(()) } - pub async fn save_local_plugin(&self, path: &str) -> anyhow::Result { + pub fn save_local_plugin(&self, path: &str) -> anyhow::Result { tracing::info!(target = "plugin", "Saving local plugin at path: {:?}", path); - let plugin_id = self.plugin_downloader.save_local_plugin(path).await?; + let plugin_id = self.plugin_downloader.save_local_plugin(path)?; let plugin = self.db_repository.get_plugin_by_id(&plugin_id.to_string())?; - self.reload_plugin(plugin_id.clone()).await?; + self.reload_plugin(plugin_id.clone())?; let (stdout_file_path, stderr_file_path) = self.dirs.plugin_log_files(&plugin.uuid); @@ -356,19 +373,26 @@ impl ApplicationManager { }) } - pub async fn load_bundled_plugins(&self) -> anyhow::Result<()> { + pub fn load_bundled_plugins(&self) -> anyhow::Result<()> { for (id, dir) in &BUNDLED_PLUGINS { tracing::info!(target = "plugin", "Saving builtin plugin with id: {:?}", id); - let plugin_id = self.plugin_downloader.save_bundled_plugin(id, dir).await?; + let plugin_id = self.plugin_downloader.save_bundled_plugin(id, dir)?; - self.reload_plugin(plugin_id).await?; + self.reload_plugin(plugin_id)?; } Ok(()) } - pub async fn plugins(&self) -> anyhow::Result> { + pub fn handle_global_shortcut_event( + &self, + event: GlobalShortcutPressedEvent, + ) -> anyhow::Result { + self.settings.handle_global_shortcut_event(event) + } + + pub fn plugins(&self) -> anyhow::Result> { let mut generator_data: HashMap<_, _> = self .search_index .plugin_entrypoint_data() @@ -485,9 +509,9 @@ impl ApplicationManager { Ok(result) } - pub async fn set_plugin_state(&self, plugin_id: PluginId, set_enabled: bool) -> anyhow::Result<()> { + pub fn set_plugin_state(&self, plugin_id: PluginId, set_enabled: bool) -> anyhow::Result<()> { let currently_running = self.run_status_holder.is_plugin_running(&plugin_id); - let currently_enabled = self.is_plugin_enabled(&plugin_id).await?; + let currently_enabled = self.is_plugin_enabled(&plugin_id)?; tracing::info!( target = "plugin", @@ -502,15 +526,15 @@ impl ApplicationManager { (false, false, true) => { self.db_repository.set_plugin_enabled(&plugin_id.to_string(), true)?; - self.start_plugin(plugin_id).await?; + self.start_plugin(plugin_id)?; } (false, true, true) => { - self.start_plugin(plugin_id).await?; + self.start_plugin(plugin_id)?; } (true, true, false) => { self.db_repository.set_plugin_enabled(&plugin_id.to_string(), false)?; - self.stop_plugin(plugin_id.clone()).await; + self.stop_plugin(plugin_id.clone()); self.search_index.remove_for_plugin(plugin_id)?; } (true, false, _) => { @@ -545,7 +569,7 @@ impl ApplicationManager { enabled, )?; - self.reload_plugin(plugin_id.clone()).await?; + self.reload_plugin(plugin_id.clone())?; Ok(()) } @@ -554,8 +578,8 @@ impl ApplicationManager { self.settings.set_global_shortcut(shortcut).await } - pub async fn get_global_shortcut(&self) -> anyhow::Result)>> { - self.settings.global_shortcut().await + pub fn get_global_shortcut(&self) -> anyhow::Result)>> { + self.settings.global_shortcut() } pub async fn set_global_entrypoint_shortcut( @@ -569,10 +593,10 @@ impl ApplicationManager { .await } - pub async fn get_global_entrypoint_shortcut( + pub fn get_global_entrypoint_shortcut( &self, ) -> anyhow::Result)>> { - self.settings.global_entrypoint_shortcuts().await + self.settings.global_entrypoint_shortcuts() } pub async fn set_entrypoint_search_alias( @@ -608,11 +632,11 @@ impl ApplicationManager { self.settings.set_window_position_mode_setting(mode).await } - pub async fn get_window_position_mode(&self) -> anyhow::Result { - self.settings.window_position_mode_setting().await + pub fn get_window_position_mode(&self) -> anyhow::Result { + self.settings.window_position_mode_setting() } - pub async fn set_preference_value( + pub fn set_preference_value( &self, plugin_id: PluginId, entrypoint_id: Option, @@ -636,31 +660,31 @@ impl ApplicationManager { user_data, )?; - self.reload_plugin(plugin_id.clone()).await?; + self.reload_plugin(plugin_id.clone())?; Ok(()) } - pub async fn reload_config(&self) -> anyhow::Result<()> { - self.config_reader.reload_config().await?; + pub fn reload_config(&self) -> anyhow::Result<()> { + self.config_reader.reload_config()?; Ok(()) } - pub async fn reload_all_plugins(&self) -> anyhow::Result<()> { + pub fn reload_all_plugins(&self) -> anyhow::Result<()> { tracing::info!("Reloading all plugins"); - self.reload_config().await?; + self.reload_config()?; for plugin in self.db_repository.list_plugins()? { let plugin_id = PluginId::from_string(plugin.id); let running = self.run_status_holder.is_plugin_running(&plugin_id); match (running, plugin.enabled) { (false, true) => { - self.start_plugin(plugin_id).await?; + self.start_plugin(plugin_id)?; } (true, false) => { - self.stop_plugin(plugin_id.clone()).await; + self.stop_plugin(plugin_id.clone()); self.search_index.remove_for_plugin(plugin_id)?; } _ => {} @@ -670,12 +694,22 @@ impl ApplicationManager { Ok(()) } - pub async fn remove_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { + pub fn save_local_dev_plugin(&self) -> anyhow::Result<()> { + let plugin_path = concat!(env!("CARGO_MANIFEST_DIR"), "/../../dev_plugin").to_owned(); + let plugin_path = std::fs::canonicalize(plugin_path).expect("valid path"); + let plugin_path = plugin_path.to_str().expect("valid utf8"); + + self.save_local_plugin(plugin_path)?; + + Ok(()) + } + + pub fn remove_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { tracing::info!(target = "plugin", "Removing plugin with id: {:?}", plugin_id); let running = self.run_status_holder.is_plugin_running(&plugin_id); if running { - self.stop_plugin(plugin_id.clone()).await; + self.stop_plugin(plugin_id.clone()); } self.db_repository.remove_plugin(&plugin_id.to_string())?; self.search_index.remove_for_plugin(plugin_id)?; @@ -829,22 +863,22 @@ impl ApplicationManager { .expect("failed to execute settings process"); // this can fail in dev if binary was replaced by more recent compilation } - async fn reload_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { + fn reload_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { tracing::info!(target = "plugin", "Reloading plugin with id: {:?}", plugin_id); let running = self.run_status_holder.is_plugin_running(&plugin_id); if running { - self.stop_plugin(plugin_id.clone()).await; + self.stop_plugin(plugin_id.clone()); } - if self.is_plugin_enabled(&plugin_id).await? { - self.start_plugin(plugin_id).await?; + if self.is_plugin_enabled(&plugin_id)? { + self.start_plugin(plugin_id)?; } Ok(()) } - async fn is_plugin_enabled(&self, plugin_id: &PluginId) -> anyhow::Result { + fn is_plugin_enabled(&self, plugin_id: &PluginId) -> anyhow::Result { self.db_repository.is_plugin_enabled(&plugin_id.to_string()) } @@ -857,9 +891,7 @@ impl ApplicationManager { .action_shortcuts(&plugin_id.to_string(), &entrypoint_id.to_string()) } - async fn start_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { - tracing::info!(target = "plugin", "Starting plugin with id: {:?}", plugin_id); - + fn start_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { let plugin_id_str = plugin_id.to_string(); let plugin = self.db_repository.get_plugin_by_id(&plugin_id_str)?; @@ -937,7 +969,7 @@ impl ApplicationManager { Ok(()) } - async fn stop_plugin(&self, plugin_id: PluginId) { + fn stop_plugin(&self, plugin_id: PluginId) { tracing::info!(target = "plugin", "Stopping plugin with id: {:?}", plugin_id); self.run_status_holder.stop_plugin(&plugin_id) @@ -987,23 +1019,6 @@ impl ApplicationManager { } impl BackendForFrontendApi for ApplicationManager { - async fn setup_data(&self) -> RequestResult { - let result = self.setup_data().await?; - - Ok(result) - } - - async fn setup_response( - &self, - global_shortcut_error: Option, - global_entrypoint_shortcuts_errors: HashMap<(PluginId, EntrypointId), Option>, - ) -> RequestResult<()> { - self.setup_response(global_shortcut_error, global_entrypoint_shortcuts_errors) - .await?; - - Ok(()) - } - async fn search(&self, text: String, render_inline_view: bool) -> RequestResult> { let result = self.search(&text, render_inline_view)?; diff --git a/rust/server/src/plugins/settings.rs b/rust/server/src/plugins/settings.rs deleted file mode 100644 index c580543..0000000 --- a/rust/server/src/plugins/settings.rs +++ /dev/null @@ -1,420 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; - -use anyhow::anyhow; -use dark_light::Mode; -use gauntlet_common::dirs::Dirs; -use gauntlet_common::model::EntrypointId; -use gauntlet_common::model::PhysicalKey; -use gauntlet_common::model::PhysicalShortcut; -use gauntlet_common::model::PluginId; -use gauntlet_common::model::SettingsTheme; -use gauntlet_common::model::UiTheme; -use gauntlet_common::model::WindowPositionMode; -use gauntlet_common::rpc::frontend_api::FrontendApi; -use gauntlet_common::rpc::frontend_api::FrontendApiProxy; - -use crate::plugins::data_db_repository::DataDbRepository; -use crate::plugins::data_db_repository::DbSettingsEntrypointSearchAliasData; -use crate::plugins::data_db_repository::DbSettingsGlobalEntrypointShortcutData; -use crate::plugins::data_db_repository::DbSettingsGlobalShortcutData; -use crate::plugins::data_db_repository::DbSettingsShortcut; -use crate::plugins::data_db_repository::DbTheme; -use crate::plugins::data_db_repository::DbWindowPositionMode; -use crate::plugins::theme::BundledThemes; -use crate::plugins::theme::read_theme_file; - -#[derive(Clone)] -pub struct Settings { - dirs: Dirs, - repository: DataDbRepository, - frontend_api: FrontendApiProxy, - themes: Arc, -} - -impl Settings { - pub fn new(dirs: Dirs, repository: DataDbRepository, frontend_api: FrontendApiProxy) -> anyhow::Result { - Ok(Self { - dirs, - repository, - frontend_api, - themes: Arc::new(BundledThemes::new()?), - }) - } - - pub async fn global_shortcut(&self) -> anyhow::Result)>> { - let settings = self.repository.get_settings()?; - - let data = settings.global_shortcut.map(|data| { - let shortcut = PhysicalShortcut { - physical_key: PhysicalKey::from_value(data.shortcut.physical_key), - modifier_shift: data.shortcut.modifier_shift, - modifier_control: data.shortcut.modifier_control, - modifier_alt: data.shortcut.modifier_alt, - modifier_meta: data.shortcut.modifier_meta, - }; - - (shortcut, data.error) - }); - - Ok(data) - } - - pub async fn set_global_shortcut(&self, shortcut: Option) -> anyhow::Result<()> { - let err = self.frontend_api.set_global_shortcut(shortcut.clone()).await; - - let db_err = err.as_ref().map_err(|err| format!("{:#}", err)).err(); - - let mut settings = self.repository.get_settings()?; - - settings.global_shortcut = shortcut.map(|shortcut| { - DbSettingsGlobalShortcutData { - shortcut: DbSettingsShortcut { - physical_key: shortcut.physical_key.to_value(), - modifier_shift: shortcut.modifier_shift, - modifier_control: shortcut.modifier_control, - modifier_alt: shortcut.modifier_alt, - modifier_meta: shortcut.modifier_meta, - }, - error: db_err, - } - }); - - self.repository.set_settings(settings)?; - - err.map_err(Into::into) - } - - pub async fn set_global_shortcut_error(&self, error: Option) -> anyhow::Result<()> { - let mut settings = self.repository.get_settings()?; - - if let Some(data) = &mut settings.global_shortcut { - data.error = error - } - - self.repository.set_settings(settings)?; - - Ok(()) - } - - pub async fn global_entrypoint_shortcuts( - &self, - ) -> anyhow::Result)>> { - let settings = self.repository.get_settings()?; - let data: HashMap<_, _> = settings - .global_entrypoint_shortcuts - .unwrap_or_default() - .into_iter() - .map(|data| { - let shortcut = data.shortcut.shortcut; - let error = data.shortcut.error; - - let shortcut = PhysicalShortcut { - physical_key: PhysicalKey::from_value(shortcut.physical_key), - modifier_shift: shortcut.modifier_shift, - modifier_control: shortcut.modifier_control, - modifier_alt: shortcut.modifier_alt, - modifier_meta: shortcut.modifier_meta, - }; - - ( - ( - PluginId::from_string(data.plugin_id), - EntrypointId::from_string(data.entrypoint_id), - ), - (shortcut, error), - ) - }) - .collect(); - - Ok(data) - } - - pub async fn set_global_entrypoint_shortcut( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - shortcut: Option, - ) -> anyhow::Result<()> { - let err = self - .frontend_api - .set_global_entrypoint_shortcut(plugin_id.clone(), entrypoint_id.clone(), shortcut.clone()) - .await; - - let db_err = err.as_ref().map_err(|err| format!("{:#}", err)).err(); - - let mut settings = self.repository.get_settings()?; - - let mut shortcuts: HashMap<_, _> = settings - .global_entrypoint_shortcuts - .unwrap_or_default() - .into_iter() - .map(|data| { - ( - ( - PluginId::from_string(data.plugin_id), - EntrypointId::from_string(data.entrypoint_id), - ), - data.shortcut, - ) - }) - .collect(); - - match shortcut { - None => { - shortcuts.remove(&(plugin_id, entrypoint_id)); - } - Some(shortcut) => { - shortcuts.insert( - (plugin_id, entrypoint_id), - DbSettingsGlobalShortcutData { - shortcut: DbSettingsShortcut { - physical_key: shortcut.physical_key.to_value(), - modifier_shift: shortcut.modifier_shift, - modifier_control: shortcut.modifier_control, - modifier_alt: shortcut.modifier_alt, - modifier_meta: shortcut.modifier_meta, - }, - error: db_err, - }, - ); - } - } - - let global_entrypoint_shortcuts = shortcuts - .into_iter() - .map(|((plugin_id, entrypoint_id), data)| { - DbSettingsGlobalEntrypointShortcutData { - plugin_id: plugin_id.to_string(), - entrypoint_id: entrypoint_id.to_string(), - shortcut: data, - } - }) - .collect(); - - settings.global_entrypoint_shortcuts = Some(global_entrypoint_shortcuts); - - self.repository.set_settings(settings)?; - - Ok(()) - } - - pub async fn set_global_entrypoint_shortcut_error( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - error: Option, - ) -> anyhow::Result<()> { - let mut settings = self.repository.get_settings()?; - - let mut shortcuts: HashMap<_, _> = settings - .global_entrypoint_shortcuts - .unwrap_or_default() - .into_iter() - .map(|data| { - ( - ( - PluginId::from_string(data.plugin_id), - EntrypointId::from_string(data.entrypoint_id), - ), - data.shortcut, - ) - }) - .collect(); - - if let Some(data) = shortcuts.get_mut(&(plugin_id, entrypoint_id)) { - data.error = error; - }; - - let global_entrypoint_shortcuts = shortcuts - .into_iter() - .map(|((plugin_id, entrypoint_id), data)| { - DbSettingsGlobalEntrypointShortcutData { - plugin_id: plugin_id.to_string(), - entrypoint_id: entrypoint_id.to_string(), - shortcut: data, - } - }) - .collect(); - - settings.global_entrypoint_shortcuts = Some(global_entrypoint_shortcuts); - - self.repository.set_settings(settings)?; - - Ok(()) - } - - pub async fn entrypoint_search_aliases(&self) -> anyhow::Result> { - let settings = self.repository.get_settings()?; - - let data: HashMap<_, _> = settings - .entrypoint_search_aliases - .unwrap_or_default() - .into_iter() - .map(|data| { - ( - ( - PluginId::from_string(data.plugin_id), - EntrypointId::from_string(data.entrypoint_id), - ), - data.alias, - ) - }) - .collect(); - - Ok(data) - } - - pub async fn set_entrypoint_search_alias( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - alias: Option, - ) -> anyhow::Result<()> { - let mut settings = self.repository.get_settings()?; - - let mut alias_data: HashMap<_, _> = settings - .entrypoint_search_aliases - .unwrap_or_default() - .into_iter() - .map(|data| { - ( - ( - PluginId::from_string(data.plugin_id), - EntrypointId::from_string(data.entrypoint_id), - ), - data.alias, - ) - }) - .collect(); - - match alias { - None => alias_data.remove(&(plugin_id, entrypoint_id)), - Some(alias) => alias_data.insert((plugin_id, entrypoint_id), alias), - }; - - let alias_data = alias_data - .into_iter() - .map(|((plugin_id, entrypoint_id), alias)| { - DbSettingsEntrypointSearchAliasData { - plugin_id: plugin_id.to_string(), - entrypoint_id: entrypoint_id.to_string(), - alias, - } - }) - .collect(); - - settings.entrypoint_search_aliases = Some(alias_data); - - self.repository.set_settings(settings)?; - - Ok(()) - } - - pub async fn effective_theme(&self) -> anyhow::Result { - if let Some(theme) = read_theme_file(self.dirs.theme_file()) { - return Ok(theme); - }; - - // TODO config - - let settings = self.repository.get_settings()?; - - let theme = match &settings.theme { - None => self.autodetect_theme(), - Some(theme) => { - match theme { - DbTheme::MacOSLight => self.themes.macos_light_theme.clone(), - DbTheme::MacOSDark => self.themes.macos_dark_theme.clone(), - DbTheme::Legacy => self.themes.legacy_theme.clone(), - } - } - }; - - Ok(theme) - } - - pub async fn theme_setting(&self) -> anyhow::Result { - if let Some(_) = read_theme_file(self.dirs.theme_file()) { - return Ok(SettingsTheme::ThemeFile); - }; - - // TODO config - - let settings = self.repository.get_settings()?; - - match settings.theme { - None => Ok(SettingsTheme::AutoDetect), - Some(DbTheme::MacOSLight) => Ok(SettingsTheme::MacOSLight), - Some(DbTheme::MacOSDark) => Ok(SettingsTheme::MacOSDark), - Some(DbTheme::Legacy) => Ok(SettingsTheme::Legacy), - } - } - - pub async fn set_theme_setting(&self, theme: SettingsTheme) -> anyhow::Result<()> { - let mut settings = self.repository.get_settings()?; - - settings.theme = match theme { - SettingsTheme::AutoDetect => None, - SettingsTheme::MacOSLight => Some(DbTheme::MacOSLight), - SettingsTheme::MacOSDark => Some(DbTheme::MacOSDark), - SettingsTheme::Legacy => Some(DbTheme::Legacy), - // these should not be visible in settings ui - SettingsTheme::Config => Err(anyhow!("Unable to set current theme to config"))?, - SettingsTheme::ThemeFile => Err(anyhow!("Unable to set current theme to a file"))?, - }; - - let theme = match &settings.theme { - None => self.autodetect_theme(), - Some(theme) => { - match theme { - DbTheme::MacOSLight => self.themes.macos_light_theme.clone(), - DbTheme::MacOSDark => self.themes.macos_dark_theme.clone(), - DbTheme::Legacy => self.themes.legacy_theme.clone(), - } - } - }; - - self.repository.set_settings(settings)?; - - self.frontend_api.set_theme(theme).await?; - - Ok(()) - } - - pub async fn window_position_mode_setting(&self) -> anyhow::Result { - let settings = self.repository.get_settings()?; - - let window_position_mode = match &settings.window_position_mode { - None => WindowPositionMode::Static, - Some(DbWindowPositionMode::ActiveMonitor) => WindowPositionMode::ActiveMonitor, - }; - - Ok(window_position_mode) - } - - pub async fn set_window_position_mode_setting(&self, mode: WindowPositionMode) -> anyhow::Result<()> { - let mut settings = self.repository.get_settings()?; - - let window_position_mode = match mode { - WindowPositionMode::Static => None, - WindowPositionMode::ActiveMonitor => Some(DbWindowPositionMode::ActiveMonitor), - }; - - settings.window_position_mode = window_position_mode; - - self.repository.set_settings(settings)?; - - self.frontend_api.set_window_position_mode(mode).await?; - - Ok(()) - } - - fn autodetect_theme(&self) -> UiTheme { - match dark_light::detect() { - Mode::Dark => self.themes.macos_dark_theme.clone(), - Mode::Light => self.themes.macos_light_theme.clone(), - Mode::Default => self.themes.macos_dark_theme.clone(), - } - } -} diff --git a/rust/server/src/plugins/settings/global_shortcut.rs b/rust/server/src/plugins/settings/global_shortcut.rs new file mode 100644 index 0000000..164d585 --- /dev/null +++ b/rust/server/src/plugins/settings/global_shortcut.rs @@ -0,0 +1,649 @@ +use std::collections::HashMap; +use std::fmt::Debug; +use std::sync::Arc; +use std::sync::Mutex; + +use anyhow::anyhow; +use futures::Sink; +use futures::SinkExt; +use gauntlet_common::model::EntrypointId; +use gauntlet_common::model::PhysicalKey; +use gauntlet_common::model::PhysicalShortcut; +use gauntlet_common::model::PluginId; +use global_hotkey::GlobalHotKeyManager; +use global_hotkey::hotkey::Code; +use global_hotkey::hotkey::HotKey; +use global_hotkey::hotkey::Modifiers; +use tokio::runtime::Handle; + +use crate::plugins::data_db_repository::DataDbRepository; +use crate::plugins::data_db_repository::DbSettingsGlobalEntrypointShortcutData; +use crate::plugins::data_db_repository::DbSettingsGlobalShortcutData; +use crate::plugins::data_db_repository::DbSettingsShortcut; + +#[derive(Clone)] +pub struct GlobalShortcutSettings { + repository: DataDbRepository, + global_hotkey_manager: Arc, + state: Arc>, +} + +struct GlobalShortcutSettingsState { + current_global_hotkey: Option, + current_entrypoint_global_hotkeys: HashMap<(PluginId, EntrypointId), HotKey>, +} + +impl GlobalShortcutSettings { + pub fn new(db_repository: DataDbRepository) -> anyhow::Result { + Ok(Self { + repository: db_repository, + global_hotkey_manager: Arc::new(GlobalHotKeyManager::new()?), + state: Arc::new(Mutex::new(GlobalShortcutSettingsState { + current_global_hotkey: None, + current_entrypoint_global_hotkeys: HashMap::new(), + })), + }) + } + + pub fn setup(&self) -> anyhow::Result<()> { + let global_shortcut = self.global_shortcut()?.map(|(shortcut, _)| shortcut); + let global_entrypoint_shortcuts = self + .global_entrypoint_shortcuts()? + .into_iter() + .map(|((plugin_id, entrypoint_id), (shortcut, _))| ((plugin_id, entrypoint_id), shortcut)) + .collect(); + + let (hotkeys, errors) = self.setup_shortcuts(global_shortcut); + self.set_global_shortcut_error(errors)?; + + let (entrypoint_hotkeys, entrypoint_errors) = self.setup_entrypoint_shortcuts(global_entrypoint_shortcuts); + for ((plugin_id, entrypoint_id), error) in entrypoint_errors { + self.set_global_entrypoint_shortcut_error(plugin_id, entrypoint_id, error)?; + } + + let mut state = self.state.lock().map_err(|_| anyhow!("lock is poisoned"))?; + + state.current_global_hotkey = hotkeys; + state.current_entrypoint_global_hotkeys = entrypoint_hotkeys; + + Ok(()) + } + + pub fn global_shortcut(&self) -> anyhow::Result)>> { + let settings = self.repository.get_settings()?; + + let data = settings.global_shortcut.map(|data| { + let shortcut = PhysicalShortcut { + physical_key: PhysicalKey::from_value(data.shortcut.physical_key), + modifier_shift: data.shortcut.modifier_shift, + modifier_control: data.shortcut.modifier_control, + modifier_alt: data.shortcut.modifier_alt, + modifier_meta: data.shortcut.modifier_meta, + }; + + (shortcut, data.error) + }); + + Ok(data) + } + + pub fn set_global_shortcut(&self, shortcut: Option) -> anyhow::Result<()> { + let mut state = self.state.lock().map_err(|_| anyhow!("lock is poisoned"))?; + + let (hotkey, err) = self.assign_global_shortcut(state.current_global_hotkey, shortcut.clone()); + + state.current_global_hotkey = hotkey; + + match &err { + Ok(()) => { + tracing::info!("Successfully registered new global shortcut: {:?}", shortcut); + } + Err(err) => { + tracing::error!("Unable to register new global shortcut {:?}: {:?}", shortcut, err); + } + } + + let db_err = err.as_ref().map_err(|err| format!("{:#}", err)).err(); + + self.repository.mutate_settings(|mut settings| { + settings.global_shortcut = shortcut.map(|shortcut| { + DbSettingsGlobalShortcutData { + shortcut: DbSettingsShortcut { + physical_key: shortcut.physical_key.to_value(), + modifier_shift: shortcut.modifier_shift, + modifier_control: shortcut.modifier_control, + modifier_alt: shortcut.modifier_alt, + modifier_meta: shortcut.modifier_meta, + }, + error: db_err, + } + }); + + Ok(settings) + })?; + + err.map_err(Into::into) + } + + fn set_global_shortcut_error(&self, error: Option) -> anyhow::Result<()> { + self.repository.mutate_settings(|mut settings| { + if let Some(data) = &mut settings.global_shortcut { + data.error = error + } + Ok(settings) + })?; + + Ok(()) + } + + pub fn global_entrypoint_shortcuts( + &self, + ) -> anyhow::Result)>> { + let settings = self.repository.get_settings()?; + let data: HashMap<_, _> = settings + .global_entrypoint_shortcuts + .unwrap_or_default() + .into_iter() + .map(|data| { + let shortcut = data.shortcut.shortcut; + let error = data.shortcut.error; + + let shortcut = PhysicalShortcut { + physical_key: PhysicalKey::from_value(shortcut.physical_key), + modifier_shift: shortcut.modifier_shift, + modifier_control: shortcut.modifier_control, + modifier_alt: shortcut.modifier_alt, + modifier_meta: shortcut.modifier_meta, + }; + + ( + ( + PluginId::from_string(data.plugin_id), + EntrypointId::from_string(data.entrypoint_id), + ), + (shortcut, error), + ) + }) + .collect(); + + Ok(data) + } + + pub fn set_global_entrypoint_shortcut( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + ) -> anyhow::Result<()> { + let mut state = self.state.lock().map_err(|_| anyhow!("lock is poisoned"))?; + + let current_global_hotkey = state + .current_entrypoint_global_hotkeys + .get(&(plugin_id.clone(), entrypoint_id.clone())) + .cloned(); + + let (hotkey, err) = self.assign_global_shortcut(current_global_hotkey, shortcut.clone()); + + if let Some(hotkey) = hotkey { + state + .current_entrypoint_global_hotkeys + .insert((plugin_id.clone(), entrypoint_id.clone()), hotkey); + } else { + state + .current_entrypoint_global_hotkeys + .remove(&(plugin_id.clone(), entrypoint_id.clone())); + }; + + match &err { + Ok(()) => { + tracing::info!( + "Successfully registered new global shortcut for plugin '{:?}' and entrypoint '{:?}' : {:?}", + plugin_id, + entrypoint_id, + shortcut + ); + } + Err(err) => { + tracing::info!( + "Unable to register new global shortcut for plugin '{:?}' and entrypoint '{:?}' - {:?}: {:?}", + plugin_id, + entrypoint_id, + shortcut, + err + ); + } + } + + let db_err = err.as_ref().map_err(|err| format!("{:#}", err)).err(); + + self.repository.mutate_settings(|mut settings| { + let mut shortcuts: HashMap<_, _> = settings + .global_entrypoint_shortcuts + .unwrap_or_default() + .into_iter() + .map(|data| { + ( + ( + PluginId::from_string(data.plugin_id), + EntrypointId::from_string(data.entrypoint_id), + ), + data.shortcut, + ) + }) + .collect(); + + match shortcut { + None => { + shortcuts.remove(&(plugin_id, entrypoint_id)); + } + Some(shortcut) => { + shortcuts.insert( + (plugin_id, entrypoint_id), + DbSettingsGlobalShortcutData { + shortcut: DbSettingsShortcut { + physical_key: shortcut.physical_key.to_value(), + modifier_shift: shortcut.modifier_shift, + modifier_control: shortcut.modifier_control, + modifier_alt: shortcut.modifier_alt, + modifier_meta: shortcut.modifier_meta, + }, + error: db_err, + }, + ); + } + } + + let global_entrypoint_shortcuts = shortcuts + .into_iter() + .map(|((plugin_id, entrypoint_id), data)| { + DbSettingsGlobalEntrypointShortcutData { + plugin_id: plugin_id.to_string(), + entrypoint_id: entrypoint_id.to_string(), + shortcut: data, + } + }) + .collect(); + + settings.global_entrypoint_shortcuts = Some(global_entrypoint_shortcuts); + + Ok(settings) + })?; + + Ok(()) + } + + fn set_global_entrypoint_shortcut_error( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + error: Option, + ) -> anyhow::Result<()> { + self.repository.mutate_settings(|mut settings| { + let mut shortcuts: HashMap<_, _> = settings + .global_entrypoint_shortcuts + .unwrap_or_default() + .into_iter() + .map(|data| { + ( + ( + PluginId::from_string(data.plugin_id), + EntrypointId::from_string(data.entrypoint_id), + ), + data.shortcut, + ) + }) + .collect(); + + if let Some(data) = shortcuts.get_mut(&(plugin_id, entrypoint_id)) { + data.error = error; + }; + + let global_entrypoint_shortcuts = shortcuts + .into_iter() + .map(|((plugin_id, entrypoint_id), data)| { + DbSettingsGlobalEntrypointShortcutData { + plugin_id: plugin_id.to_string(), + entrypoint_id: entrypoint_id.to_string(), + shortcut: data, + } + }) + .collect(); + + settings.global_entrypoint_shortcuts = Some(global_entrypoint_shortcuts); + + Ok(settings) + })?; + + Ok(()) + } + + fn assign_global_shortcut( + &self, + current_hotkey: Option, + new_shortcut: Option, + ) -> (Option, anyhow::Result<()>) { + if let Some(current_hotkey) = current_hotkey { + if let Err(err) = self.global_hotkey_manager.unregister(current_hotkey.clone()) { + tracing::warn!( + "error occurred when unregistering global shortcut {:?}: {:?}", + current_hotkey, + err + ) + } + } + + if let Some(new_shortcut) = new_shortcut { + let hotkey = convert_physical_shortcut_to_hotkey(new_shortcut); + match self.global_hotkey_manager.register(hotkey) { + Ok(()) => (Some(hotkey), Ok(())), + Err(err) => (None, Err(anyhow!(err))), + } + } else { + (None, Ok(())) + } + } + + fn setup_shortcuts(&self, global_shortcut: Option) -> (Option, Option) { + let (current_global_hotkey, result) = self.assign_global_shortcut(None, global_shortcut); + + let result = result.map_err(|err| format!("{:#}", err)).err(); + + (current_global_hotkey, result) + } + + fn setup_entrypoint_shortcuts( + &self, + global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), PhysicalShortcut>, + ) -> ( + HashMap<(PluginId, EntrypointId), HotKey>, + HashMap<(PluginId, EntrypointId), Option>, + ) { + let mut results = HashMap::new(); + let mut current_entrypoint_global_hotkeys = HashMap::new(); + + for ((plugin_id, entrypoint_id), shortcut) in global_entrypoint_shortcuts { + let (global_hotkey, result) = self.assign_global_shortcut(None, Some(shortcut)); + + if let Some(global_hotkey) = global_hotkey { + current_entrypoint_global_hotkeys.insert((plugin_id.clone(), entrypoint_id.clone()), global_hotkey); + } + + let result = result.map_err(|err| format!("{:#}", err)).err(); + results.insert((plugin_id, entrypoint_id), result); + } + + (current_entrypoint_global_hotkeys, results) + } + + pub fn handle_global_shortcut_event( + &self, + GlobalShortcutPressedEvent(id): GlobalShortcutPressedEvent, + ) -> anyhow::Result { + let state = self.state.lock().map_err(|_| anyhow!("lock is poisoned"))?; + + if let Some(hotkey) = state.current_global_hotkey { + if hotkey.id == id { + return Ok(GlobalShortcutAction::ToggleWindow); + } + } + + let ids = state + .current_entrypoint_global_hotkeys + .iter() + .find(|(_, hotkey)| hotkey.id == id) + .map(|(ids, _)| ids); + + if let Some((plugin_id, entrypoint_id)) = ids { + return Ok(GlobalShortcutAction::RunEntrypoint { + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + }); + }; + + Ok(GlobalShortcutAction::Noop) + } +} + +pub enum GlobalShortcutAction { + ToggleWindow, + RunEntrypoint { + plugin_id: PluginId, + entrypoint_id: EntrypointId, + }, + Noop, +} + +#[derive(Debug, Clone)] +pub struct GlobalShortcutPressedEvent(u32); + +pub fn register_listener + Unpin + Send + Sync + Debug + Clone + 'static>( + pressed_events: S, +) where + >::Error: Debug, +{ + let handle = Handle::current(); + + global_hotkey::GlobalHotKeyEvent::set_event_handler(Some(move |e: global_hotkey::GlobalHotKeyEvent| { + let mut pressed_events = pressed_events.clone(); + + if let global_hotkey::HotKeyState::Pressed = e.state() { + handle.spawn(async move { + if let Err(err) = pressed_events.send(GlobalShortcutPressedEvent(e.id)).await { + tracing::warn!(target = "rpc", "error occurred when receiving shortcut event {:?}", err) + } + }); + } + })); +} + +fn convert_physical_shortcut_to_hotkey(shortcut: PhysicalShortcut) -> HotKey { + let modifiers: Modifiers = { + let mut modifiers = Modifiers::empty(); + + if shortcut.modifier_alt { + modifiers.insert(Modifiers::ALT); + } + + if shortcut.modifier_control { + modifiers.insert(Modifiers::CONTROL); + } + + if shortcut.modifier_meta { + modifiers.insert(Modifiers::META); + } + + if shortcut.modifier_shift { + modifiers.insert(Modifiers::SHIFT); + } + + modifiers + }; + + let code = match shortcut.physical_key { + PhysicalKey::Backquote => Code::Backquote, + PhysicalKey::Backslash => Code::Backslash, + PhysicalKey::BracketLeft => Code::BracketLeft, + PhysicalKey::BracketRight => Code::BracketRight, + PhysicalKey::Comma => Code::Comma, + PhysicalKey::Digit1 => Code::Digit1, + PhysicalKey::Digit2 => Code::Digit2, + PhysicalKey::Digit3 => Code::Digit3, + PhysicalKey::Digit4 => Code::Digit4, + PhysicalKey::Digit5 => Code::Digit5, + PhysicalKey::Digit6 => Code::Digit6, + PhysicalKey::Digit7 => Code::Digit7, + PhysicalKey::Digit8 => Code::Digit8, + PhysicalKey::Digit9 => Code::Digit9, + PhysicalKey::Digit0 => Code::Digit0, + PhysicalKey::Equal => Code::Equal, + PhysicalKey::IntlBackslash => Code::IntlBackslash, + PhysicalKey::IntlRo => Code::IntlRo, + PhysicalKey::IntlYen => Code::IntlYen, + PhysicalKey::KeyA => Code::KeyA, + PhysicalKey::KeyB => Code::KeyB, + PhysicalKey::KeyC => Code::KeyC, + PhysicalKey::KeyD => Code::KeyD, + PhysicalKey::KeyE => Code::KeyE, + PhysicalKey::KeyF => Code::KeyF, + PhysicalKey::KeyG => Code::KeyG, + PhysicalKey::KeyH => Code::KeyH, + PhysicalKey::KeyI => Code::KeyI, + PhysicalKey::KeyJ => Code::KeyJ, + PhysicalKey::KeyK => Code::KeyK, + PhysicalKey::KeyL => Code::KeyL, + PhysicalKey::KeyM => Code::KeyM, + PhysicalKey::KeyN => Code::KeyN, + PhysicalKey::KeyO => Code::KeyO, + PhysicalKey::KeyP => Code::KeyP, + PhysicalKey::KeyQ => Code::KeyQ, + PhysicalKey::KeyR => Code::KeyR, + PhysicalKey::KeyS => Code::KeyS, + PhysicalKey::KeyT => Code::KeyT, + PhysicalKey::KeyU => Code::KeyU, + PhysicalKey::KeyV => Code::KeyV, + PhysicalKey::KeyW => Code::KeyW, + PhysicalKey::KeyX => Code::KeyX, + PhysicalKey::KeyY => Code::KeyY, + PhysicalKey::KeyZ => Code::KeyZ, + PhysicalKey::Minus => Code::Minus, + PhysicalKey::Period => Code::Period, + PhysicalKey::Quote => Code::Quote, + PhysicalKey::Semicolon => Code::Semicolon, + PhysicalKey::Slash => Code::Slash, + PhysicalKey::Backspace => Code::Backspace, + PhysicalKey::CapsLock => Code::CapsLock, + PhysicalKey::ContextMenu => Code::ContextMenu, + PhysicalKey::Enter => Code::Enter, + PhysicalKey::Space => Code::Space, + PhysicalKey::Tab => Code::Tab, + PhysicalKey::Convert => Code::Convert, + PhysicalKey::KanaMode => Code::KanaMode, + PhysicalKey::Lang1 => Code::Lang1, + PhysicalKey::Lang2 => Code::Lang2, + PhysicalKey::Lang3 => Code::Lang3, + PhysicalKey::Lang4 => Code::Lang4, + PhysicalKey::Lang5 => Code::Lang5, + PhysicalKey::NonConvert => Code::NonConvert, + PhysicalKey::Delete => Code::Delete, + PhysicalKey::End => Code::End, + PhysicalKey::Help => Code::Help, + PhysicalKey::Home => Code::Home, + PhysicalKey::Insert => Code::Insert, + PhysicalKey::PageDown => Code::PageDown, + PhysicalKey::PageUp => Code::PageUp, + PhysicalKey::ArrowDown => Code::ArrowDown, + PhysicalKey::ArrowLeft => Code::ArrowLeft, + PhysicalKey::ArrowRight => Code::ArrowRight, + PhysicalKey::ArrowUp => Code::ArrowUp, + PhysicalKey::NumLock => Code::NumLock, + PhysicalKey::Numpad0 => Code::Numpad0, + PhysicalKey::Numpad1 => Code::Numpad1, + PhysicalKey::Numpad2 => Code::Numpad2, + PhysicalKey::Numpad3 => Code::Numpad3, + PhysicalKey::Numpad4 => Code::Numpad4, + PhysicalKey::Numpad5 => Code::Numpad5, + PhysicalKey::Numpad6 => Code::Numpad6, + PhysicalKey::Numpad7 => Code::Numpad7, + PhysicalKey::Numpad8 => Code::Numpad8, + PhysicalKey::Numpad9 => Code::Numpad9, + PhysicalKey::NumpadAdd => Code::NumpadAdd, + PhysicalKey::NumpadBackspace => Code::NumpadBackspace, + PhysicalKey::NumpadClear => Code::NumpadClear, + PhysicalKey::NumpadClearEntry => Code::NumpadClearEntry, + PhysicalKey::NumpadComma => Code::NumpadComma, + PhysicalKey::NumpadDecimal => Code::NumpadDecimal, + PhysicalKey::NumpadDivide => Code::NumpadDivide, + PhysicalKey::NumpadEnter => Code::NumpadEnter, + PhysicalKey::NumpadEqual => Code::NumpadEqual, + PhysicalKey::NumpadHash => Code::NumpadHash, + PhysicalKey::NumpadMemoryAdd => Code::NumpadMemoryAdd, + PhysicalKey::NumpadMemoryClear => Code::NumpadMemoryClear, + PhysicalKey::NumpadMemoryRecall => Code::NumpadMemoryRecall, + PhysicalKey::NumpadMemoryStore => Code::NumpadMemoryStore, + PhysicalKey::NumpadMemorySubtract => Code::NumpadMemorySubtract, + PhysicalKey::NumpadMultiply => Code::NumpadMultiply, + PhysicalKey::NumpadParenLeft => Code::NumpadParenLeft, + PhysicalKey::NumpadParenRight => Code::NumpadParenRight, + PhysicalKey::NumpadStar => Code::NumpadStar, + PhysicalKey::NumpadSubtract => Code::NumpadSubtract, + PhysicalKey::Escape => Code::Escape, + PhysicalKey::Fn => Code::Fn, + PhysicalKey::FnLock => Code::FnLock, + PhysicalKey::PrintScreen => Code::PrintScreen, + PhysicalKey::ScrollLock => Code::ScrollLock, + PhysicalKey::Pause => Code::Pause, + PhysicalKey::BrowserBack => Code::BrowserBack, + PhysicalKey::BrowserFavorites => Code::BrowserFavorites, + PhysicalKey::BrowserForward => Code::BrowserForward, + PhysicalKey::BrowserHome => Code::BrowserHome, + PhysicalKey::BrowserRefresh => Code::BrowserRefresh, + PhysicalKey::BrowserSearch => Code::BrowserSearch, + PhysicalKey::BrowserStop => Code::BrowserStop, + PhysicalKey::Eject => Code::Eject, + PhysicalKey::LaunchApp1 => Code::LaunchApp1, + PhysicalKey::LaunchApp2 => Code::LaunchApp2, + PhysicalKey::LaunchMail => Code::LaunchMail, + PhysicalKey::MediaPlayPause => Code::MediaPlayPause, + PhysicalKey::MediaSelect => Code::MediaSelect, + PhysicalKey::MediaStop => Code::MediaStop, + PhysicalKey::MediaTrackNext => Code::MediaTrackNext, + PhysicalKey::MediaTrackPrevious => Code::MediaTrackPrevious, + PhysicalKey::Power => Code::Power, + PhysicalKey::Sleep => Code::Sleep, + PhysicalKey::AudioVolumeDown => Code::AudioVolumeDown, + PhysicalKey::AudioVolumeMute => Code::AudioVolumeMute, + PhysicalKey::AudioVolumeUp => Code::AudioVolumeUp, + PhysicalKey::WakeUp => Code::WakeUp, + PhysicalKey::Abort => Code::Abort, + PhysicalKey::Resume => Code::Resume, + PhysicalKey::Suspend => Code::Suspend, + PhysicalKey::Again => Code::Again, + PhysicalKey::Copy => Code::Copy, + PhysicalKey::Cut => Code::Cut, + PhysicalKey::Find => Code::Find, + PhysicalKey::Open => Code::Open, + PhysicalKey::Paste => Code::Paste, + PhysicalKey::Props => Code::Props, + PhysicalKey::Select => Code::Select, + PhysicalKey::Undo => Code::Undo, + PhysicalKey::Hiragana => Code::Hiragana, + PhysicalKey::Katakana => Code::Katakana, + PhysicalKey::F1 => Code::F1, + PhysicalKey::F2 => Code::F2, + PhysicalKey::F3 => Code::F3, + PhysicalKey::F4 => Code::F4, + PhysicalKey::F5 => Code::F5, + PhysicalKey::F6 => Code::F6, + PhysicalKey::F7 => Code::F7, + PhysicalKey::F8 => Code::F8, + PhysicalKey::F9 => Code::F9, + PhysicalKey::F10 => Code::F10, + PhysicalKey::F11 => Code::F11, + PhysicalKey::F12 => Code::F12, + PhysicalKey::F13 => Code::F13, + PhysicalKey::F14 => Code::F14, + PhysicalKey::F15 => Code::F15, + PhysicalKey::F16 => Code::F16, + PhysicalKey::F17 => Code::F17, + PhysicalKey::F18 => Code::F18, + PhysicalKey::F19 => Code::F19, + PhysicalKey::F20 => Code::F20, + PhysicalKey::F21 => Code::F21, + PhysicalKey::F22 => Code::F22, + PhysicalKey::F23 => Code::F23, + PhysicalKey::F24 => Code::F24, + PhysicalKey::F25 => Code::F25, + PhysicalKey::F26 => Code::F26, + PhysicalKey::F27 => Code::F27, + PhysicalKey::F28 => Code::F28, + PhysicalKey::F29 => Code::F29, + PhysicalKey::F30 => Code::F30, + PhysicalKey::F31 => Code::F31, + PhysicalKey::F32 => Code::F32, + PhysicalKey::F33 => Code::F33, + PhysicalKey::F34 => Code::F34, + PhysicalKey::F35 => Code::F35, + }; + + HotKey::new(Some(modifiers), code) +} diff --git a/rust/server/src/plugins/settings/mod.rs b/rust/server/src/plugins/settings/mod.rs new file mode 100644 index 0000000..cd95ca0 --- /dev/null +++ b/rust/server/src/plugins/settings/mod.rs @@ -0,0 +1,258 @@ +pub mod global_shortcut; + +use std::collections::HashMap; +use std::sync::Arc; + +use anyhow::anyhow; +use dark_light::Mode; +use gauntlet_common::dirs::Dirs; +use gauntlet_common::model::EntrypointId; +use gauntlet_common::model::PhysicalShortcut; +use gauntlet_common::model::PluginId; +use gauntlet_common::model::SettingsTheme; +use gauntlet_common::model::UiTheme; +use gauntlet_common::model::WindowPositionMode; +use gauntlet_common::rpc::frontend_api::FrontendApi; +use gauntlet_common::rpc::frontend_api::FrontendApiProxy; + +use crate::plugins::data_db_repository::DataDbRepository; +use crate::plugins::data_db_repository::DbSettingsEntrypointSearchAliasData; +use crate::plugins::data_db_repository::DbTheme; +use crate::plugins::data_db_repository::DbWindowPositionMode; +use crate::plugins::settings::global_shortcut::GlobalShortcutAction; +use crate::plugins::settings::global_shortcut::GlobalShortcutPressedEvent; +use crate::plugins::settings::global_shortcut::GlobalShortcutSettings; +use crate::plugins::theme::BundledThemes; +use crate::plugins::theme::read_theme_file; + +#[derive(Clone)] +pub struct Settings { + dirs: Dirs, + repository: DataDbRepository, + frontend_api: FrontendApiProxy, + global_hotkey_settings: GlobalShortcutSettings, + themes: Arc, +} + +impl Settings { + pub fn new(dirs: Dirs, repository: DataDbRepository, frontend_api: FrontendApiProxy) -> anyhow::Result { + Ok(Self { + dirs, + repository: repository.clone(), + frontend_api, + global_hotkey_settings: GlobalShortcutSettings::new(repository)?, + themes: Arc::new(BundledThemes::new()?), + }) + } + + pub fn setup(&self) -> anyhow::Result<()> { + self.global_hotkey_settings.setup()?; + + Ok(()) + } + + pub fn handle_global_shortcut_event( + &self, + event: GlobalShortcutPressedEvent, + ) -> anyhow::Result { + self.global_hotkey_settings.handle_global_shortcut_event(event) + } + + pub fn global_shortcut(&self) -> anyhow::Result)>> { + self.global_hotkey_settings.global_shortcut() + } + + pub async fn set_global_shortcut(&self, shortcut: Option) -> anyhow::Result<()> { + self.global_hotkey_settings.set_global_shortcut(shortcut) + } + + pub fn global_entrypoint_shortcuts( + &self, + ) -> anyhow::Result)>> { + self.global_hotkey_settings.global_entrypoint_shortcuts() + } + + pub async fn set_global_entrypoint_shortcut( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + ) -> anyhow::Result<()> { + self.global_hotkey_settings + .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) + } + + pub async fn entrypoint_search_aliases(&self) -> anyhow::Result> { + let settings = self.repository.get_settings()?; + + let data: HashMap<_, _> = settings + .entrypoint_search_aliases + .unwrap_or_default() + .into_iter() + .map(|data| { + ( + ( + PluginId::from_string(data.plugin_id), + EntrypointId::from_string(data.entrypoint_id), + ), + data.alias, + ) + }) + .collect(); + + Ok(data) + } + + pub async fn set_entrypoint_search_alias( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + alias: Option, + ) -> anyhow::Result<()> { + self.repository.mutate_settings(|mut settings| { + let mut alias_data: HashMap<_, _> = settings + .entrypoint_search_aliases + .unwrap_or_default() + .into_iter() + .map(|data| { + ( + ( + PluginId::from_string(data.plugin_id), + EntrypointId::from_string(data.entrypoint_id), + ), + data.alias, + ) + }) + .collect(); + + match alias { + None => alias_data.remove(&(plugin_id, entrypoint_id)), + Some(alias) => alias_data.insert((plugin_id, entrypoint_id), alias), + }; + + let alias_data = alias_data + .into_iter() + .map(|((plugin_id, entrypoint_id), alias)| { + DbSettingsEntrypointSearchAliasData { + plugin_id: plugin_id.to_string(), + entrypoint_id: entrypoint_id.to_string(), + alias, + } + }) + .collect(); + + settings.entrypoint_search_aliases = Some(alias_data); + + Ok(settings) + })?; + + Ok(()) + } + + pub fn effective_theme(&self) -> anyhow::Result { + if let Some(theme) = read_theme_file(self.dirs.theme_file()) { + return Ok(theme); + }; + + // TODO config + + let settings = self.repository.get_settings()?; + + let theme = match &settings.theme { + None => self.autodetect_theme(), + Some(theme) => { + match theme { + DbTheme::MacOSLight => self.themes.macos_light_theme.clone(), + DbTheme::MacOSDark => self.themes.macos_dark_theme.clone(), + DbTheme::Legacy => self.themes.legacy_theme.clone(), + } + } + }; + + Ok(theme) + } + + pub async fn theme_setting(&self) -> anyhow::Result { + if let Some(_) = read_theme_file(self.dirs.theme_file()) { + return Ok(SettingsTheme::ThemeFile); + }; + + // TODO config + + let settings = self.repository.get_settings()?; + + match settings.theme { + None => Ok(SettingsTheme::AutoDetect), + Some(DbTheme::MacOSLight) => Ok(SettingsTheme::MacOSLight), + Some(DbTheme::MacOSDark) => Ok(SettingsTheme::MacOSDark), + Some(DbTheme::Legacy) => Ok(SettingsTheme::Legacy), + } + } + + pub async fn set_theme_setting(&self, theme: SettingsTheme) -> anyhow::Result<()> { + let settings = self.repository.mutate_settings(|mut settings| { + settings.theme = match &theme { + SettingsTheme::AutoDetect => None, + SettingsTheme::MacOSLight => Some(DbTheme::MacOSLight), + SettingsTheme::MacOSDark => Some(DbTheme::MacOSDark), + SettingsTheme::Legacy => Some(DbTheme::Legacy), + // these should not be visible in settings ui + SettingsTheme::Config => Err(anyhow!("Unable to set current theme to config"))?, + SettingsTheme::ThemeFile => Err(anyhow!("Unable to set current theme to a file"))?, + }; + + Ok(settings) + })?; + + let theme = match &settings.theme { + None => self.autodetect_theme(), + Some(theme) => { + match theme { + DbTheme::MacOSLight => self.themes.macos_light_theme.clone(), + DbTheme::MacOSDark => self.themes.macos_dark_theme.clone(), + DbTheme::Legacy => self.themes.legacy_theme.clone(), + } + } + }; + + self.frontend_api.set_theme(theme).await?; + + Ok(()) + } + + pub fn window_position_mode_setting(&self) -> anyhow::Result { + let settings = self.repository.get_settings()?; + + let window_position_mode = match &settings.window_position_mode { + None => WindowPositionMode::Static, + Some(DbWindowPositionMode::ActiveMonitor) => WindowPositionMode::ActiveMonitor, + }; + + Ok(window_position_mode) + } + + pub async fn set_window_position_mode_setting(&self, mode: WindowPositionMode) -> anyhow::Result<()> { + self.repository.mutate_settings(|mut settings| { + let window_position_mode = match mode { + WindowPositionMode::Static => None, + WindowPositionMode::ActiveMonitor => Some(DbWindowPositionMode::ActiveMonitor), + }; + + settings.window_position_mode = window_position_mode; + + Ok(settings) + })?; + + self.frontend_api.set_window_position_mode(mode).await?; + + Ok(()) + } + + fn autodetect_theme(&self) -> UiTheme { + match dark_light::detect() { + Mode::Dark => self.themes.macos_dark_theme.clone(), + Mode::Light => self.themes.macos_light_theme.clone(), + Mode::Default => self.themes.macos_dark_theme.clone(), + } + } +} diff --git a/rust/server/src/rpc.rs b/rust/server/src/rpc.rs index 635e6d1..bafb09f 100644 --- a/rust/server/src/rpc.rs +++ b/rust/server/src/rpc.rs @@ -61,7 +61,7 @@ impl BackendForCliApi for BackendServerImpl { #[tonic::async_trait] impl BackendForToolsApi for BackendServerImpl { async fn save_local_plugin(&self, path: String) -> RequestResult { - let result = self.application_manager.save_local_plugin(&path).await?; + let result = self.application_manager.save_local_plugin(&path)?; Ok(result) } @@ -70,7 +70,7 @@ impl BackendForToolsApi for BackendServerImpl { #[tonic::async_trait] impl BackendForSettingsApi for BackendServerImpl { async fn plugins(&self) -> RequestResult> { - let result = self.application_manager.plugins().await; + let result = self.application_manager.plugins(); if let Err(err) = &result { tracing::warn!( @@ -84,7 +84,7 @@ impl BackendForSettingsApi for BackendServerImpl { } async fn set_plugin_state(&self, plugin_id: PluginId, enabled: bool) -> RequestResult<()> { - let result = self.application_manager.set_plugin_state(plugin_id, enabled).await; + let result = self.application_manager.set_plugin_state(plugin_id, enabled); if let Err(err) = &result { tracing::warn!( @@ -136,8 +136,7 @@ impl BackendForSettingsApi for BackendServerImpl { async fn get_global_shortcut(&self) -> RequestResult<(Option, Option)> { let result = self .application_manager - .get_global_shortcut() - .await? + .get_global_shortcut()? .map(|(shortcut, error)| (Some(shortcut), error)) .unwrap_or((None, None)); @@ -161,7 +160,6 @@ impl BackendForSettingsApi for BackendServerImpl { ) -> RequestResult)>> { self.application_manager .get_global_entrypoint_shortcut() - .await .map_err(Into::into) } @@ -200,10 +198,7 @@ impl BackendForSettingsApi for BackendServerImpl { } async fn get_window_position_mode(&self) -> RequestResult { - self.application_manager - .get_window_position_mode() - .await - .map_err(Into::into) + self.application_manager.get_window_position_mode().map_err(Into::into) } async fn set_preference_value( @@ -213,10 +208,9 @@ impl BackendForSettingsApi for BackendServerImpl { preference_id: String, preference_value: PluginPreferenceUserData, ) -> RequestResult<()> { - let result = self - .application_manager - .set_preference_value(plugin_id, entrypoint_id, preference_id, preference_value) - .await; + let result = + self.application_manager + .set_preference_value(plugin_id, entrypoint_id, preference_id, preference_value); if let Err(err) = &result { tracing::warn!( @@ -230,7 +224,7 @@ impl BackendForSettingsApi for BackendServerImpl { } async fn download_plugin(&self, plugin_id: PluginId) -> RequestResult<()> { - let result = self.application_manager.download_plugin(plugin_id).await; + let result = self.application_manager.download_plugin(plugin_id); if let Err(err) = &result { tracing::warn!( @@ -248,7 +242,7 @@ impl BackendForSettingsApi for BackendServerImpl { } async fn remove_plugin(&self, plugin_id: PluginId) -> RequestResult<()> { - let result = self.application_manager.remove_plugin(plugin_id).await; + let result = self.application_manager.remove_plugin(plugin_id); if let Err(err) = &result { tracing::warn!( diff --git a/rust/server/src/search.rs b/rust/server/src/search.rs index 28774a2..541a7fe 100644 --- a/rust/server/src/search.rs +++ b/rust/server/src/search.rs @@ -27,7 +27,7 @@ use tantivy::query::TermQuery; use tantivy::schema::*; use tantivy::tokenizer::TokenizerManager; -use crate::plugins::settings::Settings; +use crate::plugins::Settings; #[derive(Clone)] pub struct SearchIndex { From a91bf3066f889588571195cb0c041b3dcde4c06f Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 15 Jun 2025 16:21:49 +0200 Subject: [PATCH 27/91] Explicitly add some iced features --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6231b8f..9a8f161 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,8 @@ edition = "2024" [workspace.dependencies] # iced -#iced = { version = "0.13.99", features = ["tokio", "lazy", "advanced", "image", "svg"] } -iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet-0.13.1", features = ["tokio", "lazy", "advanced", "image", "svg"] } +#iced = { version = "0.13.99", features = ["wgpu", "tiny-skia", "web-colors", "tokio", "lazy", "advanced", "image", "svg"] } +iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet-0.13.1", features = ["wgpu", "tiny-skia", "web-colors", "tokio", "lazy", "advanced", "image", "svg"] } #iced_fonts = { version = "0.2.99", features = ["bootstrap"] } iced_fonts = { git = "https://github.com/project-gauntlet/iced_fonts.git", branch = "gauntlet-0.13.1", features = ["bootstrap"] } #iced_layershell = "0.13.99" From 9021f0580ece3d34259a33e068bd9873d81784b3 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 15 Jun 2025 19:10:13 +0200 Subject: [PATCH 28/91] Bump iced_fonts version with fix for release build --- Cargo.lock | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3bff76e..ec250d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3533,7 +3533,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.8", + "libloading 0.7.4", ] [[package]] @@ -5835,7 +5835,7 @@ dependencies = [ [[package]] name = "iced_fonts" version = "0.2.99" -source = "git+https://github.com/project-gauntlet/iced_fonts.git?branch=gauntlet-0.13.1#d8d4a6f256465473d9099b6ec1da2424151d16d2" +source = "git+https://github.com/project-gauntlet/iced_fonts.git?branch=gauntlet-0.13.1#60d04a578a83ad8c4ff7c67f6c92190cf99b4da8" dependencies = [ "iced_core", "iced_fonts_macros", @@ -5845,10 +5845,8 @@ dependencies = [ [[package]] name = "iced_fonts_macros" version = "0.2.99" -source = "git+https://github.com/project-gauntlet/iced_fonts.git?branch=gauntlet-0.13.1#d8d4a6f256465473d9099b6ec1da2424151d16d2" +source = "git+https://github.com/project-gauntlet/iced_fonts.git?branch=gauntlet-0.13.1#60d04a578a83ad8c4ff7c67f6c92190cf99b4da8" dependencies = [ - "iced_core", - "iced_widget", "proc-macro2", "quote", "syn 2.0.101", @@ -7613,7 +7611,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.101", From e1787e7dc8f65dc1070f0aa546170d8367f47eb1 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 15 Jun 2025 19:54:25 +0200 Subject: [PATCH 29/91] Fix macOS build --- rust/client/src/ui/mod.rs | 13 ++++++++----- rust/client/src/ui/sys_tray.rs | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 91595dc..9cd2d38 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -470,8 +470,8 @@ fn run_non_wayland(minimized: bool) -> anyhow::Result<()> { .title(title) .settings(Settings { #[cfg(target_os = "macos")] - platform_specific: iced::settings::PlatformSpecific { - activation_policy: iced::settings::ActivationPolicy::Accessory, + platform_specific: iced::PlatformSpecific { + activation_policy: iced::ActivationPolicy::Accessory, activate_ignoring_other_apps: true, }, ..Default::default() @@ -2138,7 +2138,10 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { } } -fn subscription(state: &AppModel) -> Subscription { +fn subscription( + #[allow(unused)] + state: &AppModel +) -> Subscription { let events_subscription = event::listen_with(|event, status, window_id| { match status { event::Status::Ignored => Some(AppMsg::IcedEvent(window_id, event)), @@ -2235,7 +2238,7 @@ impl AppModel { }; #[cfg(not(target_os = "linux"))] - commands.push(window::change_mode(self.main_window_id, Mode::Hidden)); + commands.push(window::set_mode(self.main_window_id, Mode::Hidden)); #[cfg(target_os = "macos")] unsafe { @@ -2299,7 +2302,7 @@ impl AppModel { WindowPositionMode::Static => Task::none(), WindowPositionMode::ActiveMonitor => window::move_to_active_monitor(self.main_window_id), }, - window::change_mode(self.main_window_id, Mode::Windowed), + window::set_mode(self.main_window_id, Mode::Windowed), ]); open_task diff --git a/rust/client/src/ui/sys_tray.rs b/rust/client/src/ui/sys_tray.rs index 7b7d8af..0bd2567 100644 --- a/rust/client/src/ui/sys_tray.rs +++ b/rust/client/src/ui/sys_tray.rs @@ -1,8 +1,6 @@ use image::ImageFormat; pub fn create_tray() -> tray_icon::TrayIcon { - use global_hotkey::hotkey::CMD_OR_CTRL; - use global_hotkey::hotkey::Code; use tray_icon::TrayIconBuilder; use tray_icon::menu::AboutMetadataBuilder; use tray_icon::menu::Menu; @@ -10,6 +8,8 @@ pub fn create_tray() -> tray_icon::TrayIcon { use tray_icon::menu::MenuItem; use tray_icon::menu::PredefinedMenuItem; use tray_icon::menu::accelerator::Accelerator; + use tray_icon::menu::accelerator::CMD_OR_CTRL; + use tray_icon::menu::accelerator::Code; MenuEvent::set_event_handler(Some(|event: MenuEvent| { match event.id().as_ref() { From 8fd14b4dabc46b23ae6af575c1a5f94a51f837ba Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 16 Jun 2025 21:28:35 +0200 Subject: [PATCH 30/91] Replace usages of BackendForFrontendApi with direct calls to ApplicationManager --- rust/client/src/ui/mod.rs | 251 +++++++++-------------------- rust/common/src/rpc/backend_api.rs | 62 ------- rust/server/src/plugins/mod.rs | 169 ++----------------- 3 files changed, 92 insertions(+), 390 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 9cd2d38..03b3645 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -18,10 +18,6 @@ use gauntlet_common::model::UiRenderLocation; use gauntlet_common::model::UiTheme; use gauntlet_common::model::UiWidgetId; use gauntlet_common::model::WindowPositionMode; -use gauntlet_common::rpc::backend_api::BackendForFrontendApi; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiProxy; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiRequestData; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiResponseData; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; use gauntlet_common::scenario_convert::ui_render_location_from_scenario; @@ -32,7 +28,6 @@ use gauntlet_server::plugins::settings::global_shortcut::GlobalShortcutAction; use gauntlet_server::plugins::settings::global_shortcut::GlobalShortcutPressedEvent; use gauntlet_server::plugins::settings::global_shortcut::register_listener; use gauntlet_utils::channel::RequestError; -use gauntlet_utils::channel::RequestResult; use gauntlet_utils::channel::Responder; use gauntlet_utils::channel::channel; use iced::Event; @@ -120,7 +115,6 @@ use crate::ui::widget::root::render_root; pub struct AppModel { // logic application_manager: Arc, - backend_api: BackendForFrontendApiProxy, main_window_id: window::Id, focused: bool, opened: bool, @@ -504,8 +498,6 @@ fn run_wayland(minimized: bool) -> anyhow::Result<()> { fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, Task) { let (frontend_sender, frontend_receiver) = channel::(); - let (backend_sender, backend_receiver) = - channel::(); let frontend_receiver = Arc::new(TokioRwLock::new(frontend_receiver)); let application_manager = ApplicationManager::create(frontend_sender).expect("Unable to setup application manager"); @@ -517,14 +509,6 @@ fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, async move { application_manager.run_grpc_server().await } }); - tokio::spawn({ - let application_manager = application_manager.clone(); - - async move { application_manager.run_message_loop(backend_receiver).await } - }); - - let backend_api = BackendForFrontendApiProxy::new(backend_sender); - let setup_data = application_manager.setup().expect("Unable to setup"); let theme = GauntletComplexTheme::new(setup_data.theme); @@ -574,7 +558,6 @@ fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, AppModel { // logic application_manager, - backend_api, main_window_id, focused: false, opened: !minimized, @@ -1585,16 +1568,15 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { plugin_id, entrypoint_id, } => { - let backend_client = state.backend_api.clone(); + let application_manager = state.application_manager.clone(); - Task::perform( - async move { - let result = backend_client.run_entrypoint(plugin_id, entrypoint_id).await?; - - Ok(result) - }, - |result| handle_backend_error(result, |()| AppMsg::Noop), - ) + Task::future(async move { + application_manager + .run_action(plugin_id, entrypoint_id, ":primary".to_string()) + .await + .map(|()| AppMsg::Noop) + .unwrap_or_else(|err| AppMsg::ShowBackendError(err.into())) + }) } } } @@ -2138,10 +2120,7 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { } } -fn subscription( - #[allow(unused)] - state: &AppModel -) -> Subscription { +fn subscription(#[allow(unused)] state: &AppModel) -> Subscription { let events_subscription = event::listen_with(|event, status, window_id| { match status { event::Status::Ignored => Some(AppMsg::IcedEvent(window_id, event)), @@ -2317,42 +2296,25 @@ impl AppModel { } fn open_plugin_view(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> Task { - let backend_client = self.backend_api.clone(); + let msg = self + .application_manager + .request_render_view(plugin_id, entrypoint_id) + .map(|action_shortcuts| AppMsg::OnOpenView { action_shortcuts }) + .unwrap_or_else(|err| AppMsg::ShowBackendError(err.into())); - Task::perform( - async move { - let result = backend_client.request_view_render(plugin_id, entrypoint_id).await?; - - Ok(result) - }, - |result| handle_backend_error(result, |action_shortcuts| AppMsg::OnOpenView { action_shortcuts }), - ) + Task::done(msg) } fn close_plugin_view(&self, plugin_id: PluginId) -> Task { - let backend_client = self.backend_api.clone(); + self.application_manager.request_view_close(plugin_id); - Task::perform( - async move { - backend_client.request_view_close(plugin_id).await?; - - Ok(()) - }, - |result| handle_backend_error(result, |()| AppMsg::Noop), - ) + Task::none() } fn run_command(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> Task { - let backend_client = self.backend_api.clone(); + self.application_manager.run_command(plugin_id, entrypoint_id); - Task::perform( - async move { - backend_client.request_run_command(plugin_id, entrypoint_id).await?; - - Ok(()) - }, - |result| handle_backend_error(result, |()| AppMsg::Noop), - ) + Task::none() } fn run_generated_entrypoint( @@ -2361,18 +2323,10 @@ impl AppModel { entrypoint_id: EntrypointId, action_index: usize, ) -> Task { - let backend_client = self.backend_api.clone(); + self.application_manager + .request_run_generated_entrypoint(plugin_id, entrypoint_id, action_index); - Task::perform( - async move { - backend_client - .request_run_generated_entrypoint(plugin_id, entrypoint_id, action_index) - .await?; - - Ok(()) - }, - |result| handle_backend_error(result, |()| AppMsg::Noop), - ) + Task::none() } fn handle_plugin_event( @@ -2381,7 +2335,7 @@ impl AppModel { plugin_id: PluginId, render_location: UiRenderLocation, ) -> Task { - let backend_client = self.backend_api.clone(); + let application_manager = self.application_manager.clone(); let event = self .client_context @@ -2399,26 +2353,14 @@ impl AppModel { _ => AppMsg::Noop, }; - Task::perform( - async move { - backend_client - .send_view_event(plugin_id, widget_id, event_name, event_arguments) - .await?; + application_manager.send_view_event(plugin_id, widget_id, event_name, event_arguments); - Ok(msg) - }, - |result| handle_backend_error(result, |msg| msg), - ) + Task::done(msg) } UiViewEvent::Open { href } => { - Task::perform( - async move { - backend_client.send_open_event(plugin_id, href).await?; + application_manager.handle_open(href); - Ok(AppMsg::Noop) - }, - |result| handle_backend_error(result, |msg| msg), - ) + Task::none() } UiViewEvent::AppEvent { event } => Task::done(event), } @@ -2437,27 +2379,18 @@ impl AppModel { modifier_alt: bool, modifier_meta: bool, ) -> Task { - let backend_client = self.backend_api.clone(); + self.application_manager.handle_keyboard_event( + plugin_id, + entrypoint_id, + KeyboardEventOrigin::MainView, + physical_key, + modifier_shift, + modifier_control, + modifier_alt, + modifier_meta, + ); - Task::perform( - async move { - backend_client - .send_keyboard_event( - plugin_id, - entrypoint_id, - KeyboardEventOrigin::MainView, - physical_key, - modifier_shift, - modifier_control, - modifier_alt, - modifier_meta, - ) - .await?; - - Ok(()) - }, - |result| handle_backend_error(result, |()| AppMsg::Noop), - ) + Task::none() } fn handle_plugin_view_keyboard_event( @@ -2468,8 +2401,6 @@ impl AppModel { modifier_alt: bool, modifier_meta: bool, ) -> Task { - let backend_client = self.backend_api.clone(); - let (plugin_id, entrypoint_id) = { ( self.client_context.get_view_plugin_id(), @@ -2477,25 +2408,18 @@ impl AppModel { ) }; - Task::perform( - async move { - backend_client - .send_keyboard_event( - plugin_id, - entrypoint_id, - KeyboardEventOrigin::PluginView, - physical_key, - modifier_shift, - modifier_control, - modifier_alt, - modifier_meta, - ) - .await?; + self.application_manager.handle_keyboard_event( + plugin_id, + entrypoint_id, + KeyboardEventOrigin::PluginView, + physical_key, + modifier_shift, + modifier_control, + modifier_alt, + modifier_meta, + ); - Ok(()) - }, - |result| handle_backend_error(result, |()| AppMsg::Noop), - ) + Task::none() } fn handle_inline_plugin_view_keyboard_event( @@ -2506,8 +2430,6 @@ impl AppModel { modifier_alt: bool, modifier_meta: bool, ) -> Task { - let backend_client = self.backend_api.clone(); - let (plugin_id, entrypoint_id) = { match self.client_context.get_first_inline_view_container() { None => return Task::none(), @@ -2515,38 +2437,28 @@ impl AppModel { } }; - Task::perform( - async move { - backend_client - .send_keyboard_event( - plugin_id, - entrypoint_id, - KeyboardEventOrigin::PluginView, - physical_key, - modifier_shift, - modifier_control, - modifier_alt, - modifier_meta, - ) - .await?; + self.application_manager.handle_keyboard_event( + plugin_id, + entrypoint_id, + KeyboardEventOrigin::PluginView, + physical_key, + modifier_shift, + modifier_control, + modifier_alt, + modifier_meta, + ); - Ok(()) - }, - |result| handle_backend_error(result, |()| AppMsg::Noop), - ) + Task::none() } fn search(&self, new_prompt: String, render_inline_view: bool) -> Task { - let backend_api = self.backend_api.clone(); + let msg = self + .application_manager + .search(&new_prompt, render_inline_view) + .map(|search_result| AppMsg::SetSearchResults(search_result)) + .unwrap_or_else(|err| AppMsg::ShowBackendError(err.into())); - Task::perform( - async move { - let search_results = backend_api.search(new_prompt, render_inline_view).await?; - - Ok(search_results) - }, - |result| handle_backend_error(result, |search_results| AppMsg::SetSearchResults(search_results)), - ) + Task::done(msg) } fn open_settings_window_preferences( @@ -2554,26 +2466,20 @@ impl AppModel { plugin_id: PluginId, entrypoint_id: Option, ) -> Task { - let backend_api = self.backend_api.clone(); + self.application_manager + .open_settings_window_preferences(plugin_id, entrypoint_id); - Task::perform( - async move { - backend_api - .open_settings_window_preferences(plugin_id, entrypoint_id) - .await?; - - Ok(()) - }, - |result| handle_backend_error(result, |()| AppMsg::Noop), - ) + Task::none() } fn inline_view_shortcuts(&self) -> Task { - let backend_api = self.backend_api.clone(); + let result = self + .application_manager + .inline_view_shortcuts() + .map(|shortcuts| AppMsg::InlineViewShortcuts { shortcuts }) + .unwrap_or_else(|err| AppMsg::ShowBackendError(err.into())); - Task::perform(async move { backend_api.inline_view_shortcuts().await }, |result| { - handle_backend_error(result, |shortcuts| AppMsg::InlineViewShortcuts { shortcuts }) - }) + Task::done(result) } fn handle_shortcut_key( @@ -2806,13 +2712,6 @@ impl AppModel { } } -fn handle_backend_error(result: RequestResult, convert: impl FnOnce(T) -> AppMsg) -> AppMsg { - match result { - Ok(val) => convert(val), - Err(err) => AppMsg::ShowBackendError(err), - } -} - async fn request_loop( request_data: FrontendApiRequestData, sender: &mut futures::channel::mpsc::Sender, diff --git a/rust/common/src/rpc/backend_api.rs b/rust/common/src/rpc/backend_api.rs index 0783b28..ad0a99f 100644 --- a/rust/common/src/rpc/backend_api.rs +++ b/rust/common/src/rpc/backend_api.rs @@ -9,79 +9,17 @@ use tonic::transport::Channel; use crate::model::DownloadStatus; use crate::model::EntrypointId; -use crate::model::KeyboardEventOrigin; use crate::model::LocalSaveData; -use crate::model::PhysicalKey; use crate::model::PhysicalShortcut; use crate::model::PluginId; use crate::model::PluginPreferenceUserData; -use crate::model::SearchResult; use crate::model::SettingsPlugin; use crate::model::SettingsTheme; -use crate::model::UiPropertyValue; -use crate::model::UiWidgetId; use crate::model::WindowPositionMode; use crate::rpc::grpc::RpcBincode; use crate::rpc::grpc::RpcSaveLocalPluginRequest; use crate::rpc::grpc::rpc_backend_client::RpcBackendClient; -#[allow(async_fn_in_trait)] -#[boundary_gen(in_process)] -pub trait BackendForFrontendApi { - async fn search(&self, text: String, render_inline_view: bool) -> RequestResult>; - - async fn request_view_render( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - ) -> RequestResult>; - - async fn request_view_close(&self, plugin_id: PluginId) -> RequestResult<()>; - - async fn request_run_command(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> RequestResult<()>; - - async fn request_run_generated_entrypoint( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - action_index: usize, - ) -> RequestResult<()>; - - async fn send_view_event( - &self, - plugin_id: PluginId, - widget_id: UiWidgetId, - event_name: String, - event_arguments: Vec, - ) -> RequestResult<()>; - - async fn send_keyboard_event( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - origin: KeyboardEventOrigin, - key: PhysicalKey, - modifier_shift: bool, - modifier_control: bool, - modifier_alt: bool, - modifier_meta: bool, - ) -> RequestResult<()>; - - async fn send_open_event(&self, plugin_id: PluginId, href: String) -> RequestResult<()>; - - async fn open_settings_window(&self) -> RequestResult<()>; - - async fn open_settings_window_preferences( - &self, - plugin_id: PluginId, - entrypoint_id: Option, - ) -> RequestResult<()>; - - async fn inline_view_shortcuts(&self) -> RequestResult>>; - - async fn run_entrypoint(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> RequestResult<()>; -} - #[boundary_gen(bincode, grpc)] #[tonic::async_trait] pub trait BackendForCliApi { diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 1b03137..f062c76 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -27,10 +27,6 @@ use gauntlet_common::model::UiPropertyValue; use gauntlet_common::model::UiSetupData; use gauntlet_common::model::UiWidgetId; use gauntlet_common::model::WindowPositionMode; -use gauntlet_common::rpc::backend_api::BackendForFrontendApi; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiRequestData; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiResponseData; -use gauntlet_common::rpc::backend_api::handle_proxy_message; use gauntlet_common::rpc::backend_server::start_backend_server; use gauntlet_common::rpc::frontend_api::FrontendApi; use gauntlet_common::rpc::frontend_api::FrontendApiProxy; @@ -41,8 +37,6 @@ use gauntlet_common_plugin_runtime::model::JsPluginCode; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsExec; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsFileSystem; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsMainSearchBar; -use gauntlet_utils::channel::RequestReceiver; -use gauntlet_utils::channel::RequestResult; use gauntlet_utils::channel::RequestSender; use include_dir::Dir; use include_dir::include_dir; @@ -167,19 +161,6 @@ impl ApplicationManager { .await } - pub async fn run_message_loop( - self: &Arc, - mut backend_receiver: RequestReceiver, - ) { - loop { - let (request_data, responder) = backend_receiver.recv().await; - - let response_data = handle_proxy_message(request_data, self.as_ref()).await; - - responder.respond(response_data); - } - } - pub fn setup(&self) -> anyhow::Result { self.settings.setup()?; @@ -252,7 +233,7 @@ impl ApplicationManager { ":primary" => { match entrypoint_type { SearchResultEntrypointType::Command => { - self.handle_run_command(plugin_id, entrypoint_id).await; + self.run_command(plugin_id, entrypoint_id); } SearchResultEntrypointType::View => { self.frontend_api.open_plugin_view(plugin_id, entrypoint_id).await?; @@ -266,7 +247,7 @@ impl ApplicationManager { match action_type { EntrypointActionType::Command => { - self.handle_run_generated_entrypoint(plugin_id, entrypoint_id, 0).await; + self.request_run_generated_entrypoint(plugin_id, entrypoint_id, 0); } EntrypointActionType::View => { self.frontend_api @@ -294,7 +275,7 @@ impl ApplicationManager { match action_type { EntrypointActionType::Command => { - self.handle_run_generated_entrypoint(plugin_id, entrypoint_id, 1).await; + self.request_run_generated_entrypoint(plugin_id, entrypoint_id, 1); } EntrypointActionType::View => { self.frontend_api @@ -331,8 +312,7 @@ impl ApplicationManager { match action_data.action_type { EntrypointActionType::Command => { - self.handle_run_generated_entrypoint(plugin_id, entrypoint_id, index) - .await; + self.request_run_generated_entrypoint(plugin_id, entrypoint_id, index); } EntrypointActionType::View => { self.frontend_api @@ -722,7 +702,7 @@ impl ApplicationManager { }) } - pub async fn handle_run_command(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) { + pub fn run_command(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) { self.send_command(PluginCommand::One { id: plugin_id.clone(), data: OnePluginCommandData::RunCommand { @@ -730,10 +710,10 @@ impl ApplicationManager { }, }); - self.mark_entrypoint_frecency(plugin_id, entrypoint_id).await + self.mark_entrypoint_frecency(plugin_id, entrypoint_id) } - pub async fn handle_run_generated_entrypoint( + pub fn request_run_generated_entrypoint( &self, plugin_id: PluginId, entrypoint_id: EntrypointId, @@ -747,10 +727,10 @@ impl ApplicationManager { }, }); - self.mark_entrypoint_frecency(plugin_id, entrypoint_id).await + self.mark_entrypoint_frecency(plugin_id, entrypoint_id) } - pub async fn handle_render_view( + pub fn request_render_view( &self, plugin_id: PluginId, entrypoint_id: EntrypointId, @@ -762,22 +742,21 @@ impl ApplicationManager { }, }); - self.mark_entrypoint_frecency(plugin_id.clone(), entrypoint_id.clone()) - .await; + self.mark_entrypoint_frecency(plugin_id.clone(), entrypoint_id.clone()); - let shortcuts = self.action_shortcuts(plugin_id, entrypoint_id).await?; + let shortcuts = self.action_shortcuts(plugin_id, entrypoint_id)?; Ok(shortcuts) } - pub fn handle_view_close(&self, plugin_id: PluginId) { + pub fn request_view_close(&self, plugin_id: PluginId) { self.send_command(PluginCommand::One { id: plugin_id, data: OnePluginCommandData::CloseView, }) } - pub fn handle_view_event( + pub fn send_view_event( &self, plugin_id: PluginId, widget_id: UiWidgetId, @@ -842,7 +821,7 @@ impl ApplicationManager { .expect("failed to execute settings process"); } - pub fn handle_open_settings_window_preferences(&self, plugin_id: PluginId, entrypoint_id: Option) { + pub fn open_settings_window_preferences(&self, plugin_id: PluginId, entrypoint_id: Option) { let data = if let Some(entrypoint_id) = entrypoint_id { SettingsEnvData::OpenEntrypointPreferences { plugin_id: plugin_id.to_string(), @@ -882,7 +861,7 @@ impl ApplicationManager { self.db_repository.is_plugin_enabled(&plugin_id.to_string()) } - async fn action_shortcuts( + fn action_shortcuts( &self, plugin_id: PluginId, entrypoint_id: EntrypointId, @@ -990,7 +969,7 @@ impl ApplicationManager { let _ = self.command_broadcaster.send(command); } - async fn mark_entrypoint_frecency(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) { + fn mark_entrypoint_frecency(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) { let result = self .db_repository .mark_entrypoint_frecency(&plugin_id.to_string(), &entrypoint_id.to_string()); @@ -1006,7 +985,7 @@ impl ApplicationManager { self.request_search_index_refresh(plugin_id); } - pub async fn inline_view_shortcuts(&self) -> anyhow::Result>> { + pub fn inline_view_shortcuts(&self) -> anyhow::Result>> { let result: HashMap<_, _> = self .db_repository .inline_view_shortcuts()? @@ -1018,120 +997,6 @@ impl ApplicationManager { } } -impl BackendForFrontendApi for ApplicationManager { - async fn search(&self, text: String, render_inline_view: bool) -> RequestResult> { - let result = self.search(&text, render_inline_view)?; - - Ok(result) - } - - async fn request_view_render( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - ) -> RequestResult> { - let result = self.handle_render_view(plugin_id, entrypoint_id).await?; - - Ok(result) - } - - async fn request_view_close(&self, plugin_id: PluginId) -> RequestResult<()> { - self.handle_view_close(plugin_id); - - Ok(()) - } - - async fn request_run_command(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> RequestResult<()> { - self.handle_run_command(plugin_id, entrypoint_id).await; - - Ok(()) - } - - async fn request_run_generated_entrypoint( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - action_index: usize, - ) -> RequestResult<()> { - self.handle_run_generated_entrypoint(plugin_id, entrypoint_id, action_index) - .await; - - Ok(()) - } - - async fn send_view_event( - &self, - plugin_id: PluginId, - widget_id: UiWidgetId, - event_name: String, - event_arguments: Vec, - ) -> RequestResult<()> { - self.handle_view_event(plugin_id, widget_id, event_name, event_arguments); - - Ok(()) - } - - async fn send_keyboard_event( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - origin: KeyboardEventOrigin, - key: PhysicalKey, - modifier_shift: bool, - modifier_control: bool, - modifier_alt: bool, - modifier_meta: bool, - ) -> RequestResult<()> { - self.handle_keyboard_event( - plugin_id, - entrypoint_id, - origin, - key, - modifier_shift, - modifier_control, - modifier_alt, - modifier_meta, - ); - - Ok(()) - } - - async fn send_open_event(&self, _plugin_id: PluginId, href: String) -> RequestResult<()> { - self.handle_open(href); - - Ok(()) - } - - async fn open_settings_window(&self) -> RequestResult<()> { - self.handle_open_settings_window(); - - Ok(()) - } - - async fn open_settings_window_preferences( - &self, - plugin_id: PluginId, - entrypoint_id: Option, - ) -> RequestResult<()> { - self.handle_open_settings_window_preferences(plugin_id, entrypoint_id); - - Ok(()) - } - - async fn inline_view_shortcuts(&self) -> RequestResult>> { - let result = self.inline_view_shortcuts().await?; - - Ok(result) - } - - async fn run_entrypoint(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> RequestResult<()> { - self.run_action(plugin_id, entrypoint_id, ":primary".to_string()) - .await?; - - Ok(()) - } -} - fn plugin_preference_from_db(id: &str, value: DbPluginPreference) -> PluginPreference { match value { DbPluginPreference::Number { From 3d1beaca152e6a30d71a4f6174eceed2a15f4a6d Mon Sep 17 00:00:00 2001 From: Gabriel Berto Date: Wed, 18 Jun 2025 11:48:28 -0300 Subject: [PATCH 31/91] fix: show full cause when printing error --- rust/utils/src/channel.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/utils/src/channel.rs b/rust/utils/src/channel.rs index 8b082f1..5db5adc 100644 --- a/rust/utils/src/channel.rs +++ b/rust/utils/src/channel.rs @@ -28,7 +28,7 @@ impl From for RequestError { impl From for RequestError { fn from(error: Error) -> RequestError { RequestError::Other { - display: format!("{}", error), + display: format!("{:#}", error), } } } @@ -50,7 +50,7 @@ impl From for RequestError { impl From for RequestError { fn from(error: prost::UnknownEnumValue) -> RequestError { RequestError::Other { - display: format!("{}", error), + display: format!("{:#}", error), } } } @@ -105,7 +105,7 @@ impl RequestSender { let result = tokio::time::timeout(duration, receiver.recv()).await?.map_err(|err| { RequestError::Other { - display: format!("{}", err), + display: format!("{:#}", err), } })?; From e7f621018c3f50d9b39342f6978d90b6f6f00145 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 19 Jun 2025 11:10:02 +0200 Subject: [PATCH 32/91] Move GlobalHotkeyManager out of ApplicationManger to make it Send. Fixes build on Windows --- Cargo.lock | 4 +- rust/client/src/ui/mod.rs | 318 +++++++++++++++++- rust/common/src/model.rs | 2 +- rust/common/src/rpc/mod.rs | 1 + rust/common/src/rpc/server_grpc_api.rs | 88 +++++ rust/server/src/lib.rs | 4 +- rust/server/src/plugins/js.rs | 5 +- rust/server/src/plugins/mod.rs | 70 ++-- .../src/plugins/settings/global_shortcut.rs | 37 +- rust/server/src/plugins/settings/mod.rs | 31 +- rust/server/src/rpc.rs | 128 +++---- rust/server/src/search.rs | 6 +- rust/utils_macros/src/boundary_gen.rs | 6 +- 13 files changed, 558 insertions(+), 142 deletions(-) create mode 100644 rust/common/src/rpc/server_grpc_api.rs diff --git a/Cargo.lock b/Cargo.lock index ec250d1..a31edda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3533,7 +3533,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.7.4", + "libloading 0.8.8", ] [[package]] @@ -7611,7 +7611,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", "syn 2.0.101", diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 03b3645..0ddacba 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -1,8 +1,10 @@ use std::collections::HashMap; use std::fs; +use std::ops::Deref; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; +use std::sync::Mutex; use client_context::ClientContext; use gauntlet_common::model::EntrypointId; @@ -20,13 +22,18 @@ use gauntlet_common::model::UiWidgetId; use gauntlet_common::model::WindowPositionMode; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; +use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiProxy; +use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiRequestData; +use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiResponseData; use gauntlet_common::scenario_convert::ui_render_location_from_scenario; use gauntlet_common::scenario_model::ScenarioFrontendEvent; use gauntlet_common_ui::physical_key_model; +use gauntlet_server::global_hotkey::GlobalHotKeyManager; use gauntlet_server::plugins::ApplicationManager; use gauntlet_server::plugins::settings::global_shortcut::GlobalShortcutAction; use gauntlet_server::plugins::settings::global_shortcut::GlobalShortcutPressedEvent; use gauntlet_server::plugins::settings::global_shortcut::register_listener; +use gauntlet_server::rpc::run_grpc_server; use gauntlet_utils::channel::RequestError; use gauntlet_utils::channel::Responder; use gauntlet_utils::channel::channel; @@ -43,9 +50,9 @@ use iced::alignment::Horizontal; use iced::alignment::Vertical; use iced::event; use iced::font; -use iced::futures; use iced::futures::SinkExt; use iced::futures::StreamExt; +use iced::futures::channel::mpsc; use iced::keyboard; use iced::keyboard::Key; use iced::keyboard::Modifiers; @@ -115,6 +122,7 @@ use crate::ui::widget::root::render_root; pub struct AppModel { // logic application_manager: Arc, + global_hotkey_manager: GlobalHotKeyManager, main_window_id: window::Id, focused: bool, opened: bool, @@ -316,6 +324,10 @@ pub enum AppMsg { plugin_id: PluginId, entrypoint_id: EntrypointId, }, + HandleServerRequest { + request_data: Arc, + responder: Arc>>>, + }, } #[cfg(target_os = "linux")] @@ -498,18 +510,21 @@ fn run_wayland(minimized: bool) -> anyhow::Result<()> { fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, Task) { let (frontend_sender, frontend_receiver) = channel::(); + let (server_grpc_sender, server_grpc_receiver) = channel::(); + + let application_manager = ApplicationManager::create(frontend_sender).expect("Unable to setup application manager"); + let global_hotkey_manager = GlobalHotKeyManager::new().expect("Unable to setup shortcut manager"); + let grpc_api = ServerGrpcApiProxy::new(server_grpc_sender); let frontend_receiver = Arc::new(TokioRwLock::new(frontend_receiver)); - let application_manager = ApplicationManager::create(frontend_sender).expect("Unable to setup application manager"); + let server_grpc_receiver = Arc::new(TokioRwLock::new(server_grpc_receiver)); let application_manager = Arc::new(application_manager); - tokio::spawn({ - let application_manager = application_manager.clone(); + tokio::spawn(async move { run_grpc_server(grpc_api).await }); - async move { application_manager.run_grpc_server().await } - }); - - let setup_data = application_manager.setup().expect("Unable to setup"); + let setup_data = application_manager + .setup(&global_hotkey_manager) + .expect("Unable to setup"); let theme = GauntletComplexTheme::new(setup_data.theme); GauntletComplexTheme::set_global(theme.clone()); @@ -534,7 +549,7 @@ fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, tasks.push(open_task); - tasks.push(Task::stream(stream::channel(100, |mut sender| { + tasks.push(Task::stream(stream::channel(10, |mut sender| { async move { let mut frontend_receiver = frontend_receiver.write().await; @@ -546,6 +561,23 @@ fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, } }))); + tasks.push(Task::stream(stream::channel(10, |mut sender: mpsc::Sender| { + async move { + let mut server_grpc_receiver = server_grpc_receiver.write().await; + + loop { + let (request_data, responder) = server_grpc_receiver.recv().await; + + let app_msg = AppMsg::HandleServerRequest { + request_data: Arc::new(request_data), + responder: Arc::new(Mutex::new(Some(responder))), + }; + + let _ = sender.send(app_msg).await; + } + } + }))); + let mut client_context = ClientContext::new(); let global_state = if cfg!(feature = "scenario_runner") { @@ -558,6 +590,7 @@ fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, AppModel { // logic application_manager, + global_hotkey_manager, main_window_id, focused: false, opened: !minimized, @@ -1578,6 +1611,10 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { .unwrap_or_else(|err| AppMsg::ShowBackendError(err.into())) }) } + AppMsg::HandleServerRequest { + request_data, + responder, + } => handle_server_message(state, request_data, responder), } } @@ -2714,7 +2751,7 @@ impl AppModel { async fn request_loop( request_data: FrontendApiRequestData, - sender: &mut futures::channel::mpsc::Sender, + sender: &mut mpsc::Sender, responder: Responder, ) { let app_msg = { @@ -2846,3 +2883,264 @@ async fn request_loop( let _ = sender.send(app_msg).await; } + +fn handle_server_message( + state: &mut AppModel, + request_data: Arc, + responder: Arc>>>, +) -> Task { + let responder = responder + .lock() + .expect("lock is poisoned") + .take() + .expect("there should always be a responder here"); + + match request_data.deref() { + ServerGrpcApiRequestData::ShowWindow {} => { + responder.respond(Ok(ServerGrpcApiResponseData::ShowWindow { data: () })); + + state.show_window() + } + ServerGrpcApiRequestData::ShowSettingsWindow {} => { + responder.respond(Ok(ServerGrpcApiResponseData::ShowSettingsWindow { data: () })); + + state.application_manager.handle_open_settings_window(); + + Task::none() + } + ServerGrpcApiRequestData::RunAction { + plugin_id, + entrypoint_id, + action_id, + } => { + let application_manager = state.application_manager.clone(); + let plugin_id = plugin_id.clone(); + let entrypoint_id = entrypoint_id.clone(); + let action_id = action_id.clone(); + + Task::future(async move { + let result = application_manager + .run_action(plugin_id, entrypoint_id, action_id) + .await + .map(|data| ServerGrpcApiResponseData::RunAction { data }); + + responder.respond(result); + + AppMsg::Noop + }) + } + ServerGrpcApiRequestData::SaveLocalPlugin { path } => { + let result = state + .application_manager + .save_local_plugin(&path) + .map(|data| ServerGrpcApiResponseData::SaveLocalPlugin { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::Plugins {} => { + let result = state + .application_manager + .plugins() + .map(|data| ServerGrpcApiResponseData::Plugins { data }); + + responder.respond(result.into()); + + Task::none() + } + ServerGrpcApiRequestData::SetPluginState { plugin_id, enabled } => { + let result = state + .application_manager + .set_plugin_state(plugin_id.clone(), *enabled) + .map(|data| ServerGrpcApiResponseData::SetPluginState { data }); + + responder.respond(result.into()); + + Task::none() + } + ServerGrpcApiRequestData::SetEntrypointState { + plugin_id, + entrypoint_id, + enabled, + } => { + let result = state + .application_manager + .set_entrypoint_state(plugin_id.clone(), entrypoint_id.clone(), *enabled) + .map(|data| ServerGrpcApiResponseData::SetEntrypointState { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetGlobalShortcut { shortcut } => { + let result = state + .application_manager + .set_global_shortcut(&state.global_hotkey_manager, shortcut.clone()); + + responder.respond(Ok(ServerGrpcApiResponseData::SetGlobalShortcut { data: result })); + + Task::none() + } + ServerGrpcApiRequestData::GetGlobalShortcut {} => { + let result = state + .application_manager + .get_global_shortcut() + .map(|data| ServerGrpcApiResponseData::GetGlobalShortcut { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetGlobalEntrypointShortcut { + plugin_id, + entrypoint_id, + shortcut, + } => { + let result = state + .application_manager + .set_global_entrypoint_shortcut( + &state.global_hotkey_manager, + plugin_id.clone(), + entrypoint_id.clone(), + shortcut.clone(), + ) + .map(|data| ServerGrpcApiResponseData::SetGlobalEntrypointShortcut { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::GetGlobalEntrypointShortcuts {} => { + let result = state + .application_manager + .get_global_entrypoint_shortcut() + .map(|data| ServerGrpcApiResponseData::GetGlobalEntrypointShortcuts { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetEntrypointSearchAlias { + plugin_id, + entrypoint_id, + alias, + } => { + let result = state + .application_manager + .set_entrypoint_search_alias(plugin_id.clone(), entrypoint_id.clone(), alias.clone()) + .map(|data| ServerGrpcApiResponseData::SetEntrypointSearchAlias { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::GetEntrypointSearchAliases {} => { + let result = state + .application_manager + .get_entrypoint_search_aliases() + .map(|data| ServerGrpcApiResponseData::GetEntrypointSearchAliases { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetTheme { theme } => { + let application_manager = state.application_manager.clone(); + let theme = theme.clone(); + + Task::future(async move { + let result = application_manager + .set_theme(theme) + .await + .map(|data| ServerGrpcApiResponseData::SetTheme { data }); + + responder.respond(result); + + AppMsg::Noop + }) + } + ServerGrpcApiRequestData::GetTheme {} => { + let result = state + .application_manager + .get_theme() + .map(|data| ServerGrpcApiResponseData::GetTheme { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetWindowPositionMode { mode } => { + let application_manager = state.application_manager.clone(); + let mode = mode.clone(); + + Task::future(async move { + let result = application_manager + .set_window_position_mode(mode) + .await + .map(|data| ServerGrpcApiResponseData::SetWindowPositionMode { data }); + + responder.respond(result); + + AppMsg::Noop + }) + } + ServerGrpcApiRequestData::GetWindowPositionMode {} => { + let result = state + .application_manager + .get_window_position_mode() + .map(|data| ServerGrpcApiResponseData::GetWindowPositionMode { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetPreferenceValue { + plugin_id, + entrypoint_id, + preference_id, + preference_value, + } => { + let result = state + .application_manager + .set_preference_value( + plugin_id.clone(), + entrypoint_id.clone(), + preference_id.clone(), + preference_value.clone(), + ) + .map(|data| ServerGrpcApiResponseData::SetPreferenceValue { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::DownloadPlugin { plugin_id } => { + let result = state + .application_manager + .download_plugin(plugin_id.clone()) + .map(|data| ServerGrpcApiResponseData::DownloadPlugin { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::DownloadStatus {} => { + let data = state.application_manager.download_status(); + + responder.respond(Ok(ServerGrpcApiResponseData::DownloadStatus { data })); + + Task::none() + } + ServerGrpcApiRequestData::RemovePlugin { plugin_id } => { + let result = state + .application_manager + .remove_plugin(plugin_id.clone()) + .map(|data| ServerGrpcApiResponseData::RemovePlugin { data }); + + responder.respond(result); + + Task::none() + } + } +} diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index baaff65..f2f31a1 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -565,7 +565,7 @@ pub enum SettingsTheme { } impl Display for SettingsTheme { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let label = match self { SettingsTheme::AutoDetect => "Auto-detect", SettingsTheme::ThemeFile => "Theme file present", diff --git a/rust/common/src/rpc/mod.rs b/rust/common/src/rpc/mod.rs index 605367f..be40540 100644 --- a/rust/common/src/rpc/mod.rs +++ b/rust/common/src/rpc/mod.rs @@ -2,3 +2,4 @@ pub mod backend_api; pub mod backend_server; pub mod frontend_api; mod grpc; +pub mod server_grpc_api; diff --git a/rust/common/src/rpc/server_grpc_api.rs b/rust/common/src/rpc/server_grpc_api.rs new file mode 100644 index 0000000..182927c --- /dev/null +++ b/rust/common/src/rpc/server_grpc_api.rs @@ -0,0 +1,88 @@ +use std::collections::HashMap; + +use gauntlet_utils::channel::RequestResult; +use gauntlet_utils_macros::boundary_gen; + +use crate::model::DownloadStatus; +use crate::model::EntrypointId; +use crate::model::LocalSaveData; +use crate::model::PhysicalShortcut; +use crate::model::PluginId; +use crate::model::PluginPreferenceUserData; +use crate::model::SettingsPlugin; +use crate::model::SettingsTheme; +use crate::model::WindowPositionMode; + +#[allow(async_fn_in_trait)] +#[boundary_gen(in_process)] +pub trait ServerGrpcApi { + async fn show_window(&self) -> RequestResult<()>; + + async fn show_settings_window(&self) -> RequestResult<()>; + + async fn run_action( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + action_id: String, + ) -> RequestResult<()>; + + async fn save_local_plugin(&self, path: String) -> RequestResult; + + async fn plugins(&self) -> RequestResult>; + + async fn set_plugin_state(&self, plugin_id: PluginId, enabled: bool) -> RequestResult<()>; + + async fn set_entrypoint_state( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + enabled: bool, + ) -> RequestResult<()>; + + async fn set_global_shortcut(&self, shortcut: Option) -> RequestResult>; + + async fn get_global_shortcut(&self) -> RequestResult)>>; + + async fn set_global_entrypoint_shortcut( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + ) -> RequestResult<()>; + + async fn get_global_entrypoint_shortcuts( + &self, + ) -> RequestResult)>>; + + async fn set_entrypoint_search_alias( + &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + alias: Option, + ) -> RequestResult<()>; + + async fn get_entrypoint_search_aliases(&self) -> RequestResult>; + + async fn set_theme(&self, theme: SettingsTheme) -> RequestResult<()>; + + async fn get_theme(&self) -> RequestResult; + + async fn set_window_position_mode(&self, mode: WindowPositionMode) -> RequestResult<()>; + + async fn get_window_position_mode(&self) -> RequestResult; + + async fn set_preference_value( + &self, + plugin_id: PluginId, + entrypoint_id: Option, + preference_id: String, + preference_value: PluginPreferenceUserData, + ) -> RequestResult<()>; + + async fn download_plugin(&self, plugin_id: PluginId) -> RequestResult<()>; + + async fn download_status(&self) -> RequestResult>; + + async fn remove_plugin(&self, plugin_id: PluginId) -> RequestResult<()>; +} diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 91bd26e..6e7e2cd 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -1,8 +1,10 @@ mod model; pub mod plugins; -mod rpc; +pub mod rpc; mod search; +pub use global_hotkey; + pub const PLUGIN_CONNECT_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_CONNECT__"; pub const PLUGIN_UUID_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_UUID__"; diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index c5e3970..22dbb38 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -19,7 +19,7 @@ use gauntlet_common::rpc::frontend_api::FrontendApi; use gauntlet_common::rpc::frontend_api::FrontendApiProxy; use gauntlet_common_plugin_runtime::JsMessageSide; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; -use gauntlet_common_plugin_runtime::api::handle_proxy_message; +use gauntlet_common_plugin_runtime::api::handle_proxy_message_backend_for_plugin_runtime_api; use gauntlet_common_plugin_runtime::model::JsClipboardData; use gauntlet_common_plugin_runtime::model::JsEvent; use gauntlet_common_plugin_runtime::model::JsGeneratedSearchItem; @@ -486,7 +486,7 @@ async fn request_loop( match message { JsPluginRuntimeMessage::Stopped => Ok(true), JsPluginRuntimeMessage::Request(message) => { - match handle_proxy_message(message, api).await { + match handle_proxy_message_backend_for_plugin_runtime_api(message, api).await { Ok(response) => { let mut send = send.lock().await; @@ -804,7 +804,6 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { generated_search_items, refresh_search_list, ) - .await .context("error when updating search index")?; Ok(()) diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index f062c76..d9377e3 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::sync::Arc; use anyhow::anyhow; use gauntlet_common::SETTINGS_ENV; @@ -27,7 +26,6 @@ use gauntlet_common::model::UiPropertyValue; use gauntlet_common::model::UiSetupData; use gauntlet_common::model::UiWidgetId; use gauntlet_common::model::WindowPositionMode; -use gauntlet_common::rpc::backend_server::start_backend_server; use gauntlet_common::rpc::frontend_api::FrontendApi; use gauntlet_common::rpc::frontend_api::FrontendApiProxy; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; @@ -38,6 +36,7 @@ use gauntlet_common_plugin_runtime::model::JsPluginPermissionsExec; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsFileSystem; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsMainSearchBar; use gauntlet_utils::channel::RequestSender; +use global_hotkey::GlobalHotKeyManager; use include_dir::Dir; use include_dir::include_dir; use itertools::Itertools; @@ -64,7 +63,6 @@ use crate::plugins::run_status::RunStatusHolder; pub(crate) use crate::plugins::settings::Settings; use crate::plugins::settings::global_shortcut::GlobalShortcutAction; use crate::plugins::settings::global_shortcut::GlobalShortcutPressedEvent; -use crate::rpc::BackendServerImpl; use crate::search::EntrypointActionDataView; use crate::search::EntrypointActionType; use crate::search::EntrypointDataView; @@ -152,17 +150,8 @@ impl ApplicationManager { Ok(application_manager) } - pub async fn run_grpc_server(self: &Arc) { - start_backend_server( - Box::new(BackendServerImpl::new(self.clone())), - Box::new(BackendServerImpl::new(self.clone())), - Box::new(BackendServerImpl::new(self.clone())), - ) - .await - } - - pub fn setup(&self) -> anyhow::Result { - self.settings.setup()?; + pub fn setup(&self, global_hotkey_manager: &GlobalHotKeyManager) -> anyhow::Result { + self.settings.setup(global_hotkey_manager)?; let window_position_file = self.dirs.window_position(); let theme = self.settings.effective_theme()?; @@ -529,7 +518,7 @@ impl ApplicationManager { Ok(()) } - pub async fn set_entrypoint_state( + pub fn set_entrypoint_state( &self, plugin_id: PluginId, entrypoint_id: EntrypointId, @@ -554,23 +543,30 @@ impl ApplicationManager { Ok(()) } - pub async fn set_global_shortcut(&self, shortcut: Option) -> anyhow::Result<()> { - self.settings.set_global_shortcut(shortcut).await + pub fn set_global_shortcut( + &self, + global_hotkey_manager: &GlobalHotKeyManager, + shortcut: Option, + ) -> Option { + self.settings + .set_global_shortcut(global_hotkey_manager, shortcut) + .err() + .map(|err| format!("{:#}", err)) } pub fn get_global_shortcut(&self) -> anyhow::Result)>> { self.settings.global_shortcut() } - pub async fn set_global_entrypoint_shortcut( + pub fn set_global_entrypoint_shortcut( &self, + global_hotkey_manager: &GlobalHotKeyManager, plugin_id: PluginId, entrypoint_id: EntrypointId, shortcut: Option, ) -> anyhow::Result<()> { self.settings - .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) - .await + .set_global_entrypoint_shortcut(global_hotkey_manager, plugin_id, entrypoint_id, shortcut) } pub fn get_global_entrypoint_shortcut( @@ -579,33 +575,31 @@ impl ApplicationManager { self.settings.global_entrypoint_shortcuts() } - pub async fn set_entrypoint_search_alias( + pub fn set_entrypoint_search_alias( &self, plugin_id: PluginId, entrypoint_id: EntrypointId, alias: Option, ) -> anyhow::Result<()> { self.settings - .set_entrypoint_search_alias(plugin_id.clone(), entrypoint_id.clone(), alias.clone()) - .await?; + .set_entrypoint_search_alias(plugin_id.clone(), entrypoint_id.clone(), alias.clone())?; self.search_index - .set_entrypoint_search_alias(plugin_id, entrypoint_id, alias) - .await?; + .set_entrypoint_search_alias(plugin_id, entrypoint_id, alias)?; Ok(()) } - pub async fn get_entrypoint_search_aliases(&self) -> anyhow::Result> { - self.settings.entrypoint_search_aliases().await + pub fn get_entrypoint_search_aliases(&self) -> anyhow::Result> { + self.settings.entrypoint_search_aliases() } pub async fn set_theme(&self, theme: SettingsTheme) -> anyhow::Result<()> { self.settings.set_theme_setting(theme).await } - pub async fn get_theme(&self) -> anyhow::Result { - self.settings.theme_setting().await + pub fn get_theme(&self) -> anyhow::Result { + self.settings.theme_setting() } pub async fn set_window_position_mode(&self, mode: WindowPositionMode) -> anyhow::Result<()> { @@ -943,7 +937,13 @@ impl ApplicationManager { clipboard: self.clipboard.clone(), }; - self.start_plugin_runtime(data); + let run_status_guard = self.run_status_holder.start_block(data.id.clone()); + + tokio::spawn(async { + start_plugin_runtime(data, run_status_guard) + .await + .expect("failed to start plugin runtime") + }); Ok(()) } @@ -954,16 +954,6 @@ impl ApplicationManager { self.run_status_holder.stop_plugin(&plugin_id) } - fn start_plugin_runtime(&self, data: PluginRuntimeData) { - let run_status_guard = self.run_status_holder.start_block(data.id.clone()); - - tokio::spawn(async { - start_plugin_runtime(data, run_status_guard) - .await - .expect("failed to start plugin runtime") - }); - } - fn send_command(&self, command: PluginCommand) { // it is possible to have 0 plugins let _ = self.command_broadcaster.send(command); diff --git a/rust/server/src/plugins/settings/global_shortcut.rs b/rust/server/src/plugins/settings/global_shortcut.rs index 164d585..76cf397 100644 --- a/rust/server/src/plugins/settings/global_shortcut.rs +++ b/rust/server/src/plugins/settings/global_shortcut.rs @@ -24,7 +24,6 @@ use crate::plugins::data_db_repository::DbSettingsShortcut; #[derive(Clone)] pub struct GlobalShortcutSettings { repository: DataDbRepository, - global_hotkey_manager: Arc, state: Arc>, } @@ -37,7 +36,6 @@ impl GlobalShortcutSettings { pub fn new(db_repository: DataDbRepository) -> anyhow::Result { Ok(Self { repository: db_repository, - global_hotkey_manager: Arc::new(GlobalHotKeyManager::new()?), state: Arc::new(Mutex::new(GlobalShortcutSettingsState { current_global_hotkey: None, current_entrypoint_global_hotkeys: HashMap::new(), @@ -45,7 +43,7 @@ impl GlobalShortcutSettings { }) } - pub fn setup(&self) -> anyhow::Result<()> { + pub fn setup(&self, global_hotkey_manager: &GlobalHotKeyManager) -> anyhow::Result<()> { let global_shortcut = self.global_shortcut()?.map(|(shortcut, _)| shortcut); let global_entrypoint_shortcuts = self .global_entrypoint_shortcuts()? @@ -53,10 +51,11 @@ impl GlobalShortcutSettings { .map(|((plugin_id, entrypoint_id), (shortcut, _))| ((plugin_id, entrypoint_id), shortcut)) .collect(); - let (hotkeys, errors) = self.setup_shortcuts(global_shortcut); + let (hotkeys, errors) = self.setup_shortcuts(global_hotkey_manager, global_shortcut); self.set_global_shortcut_error(errors)?; - let (entrypoint_hotkeys, entrypoint_errors) = self.setup_entrypoint_shortcuts(global_entrypoint_shortcuts); + let (entrypoint_hotkeys, entrypoint_errors) = + self.setup_entrypoint_shortcuts(global_hotkey_manager, global_entrypoint_shortcuts); for ((plugin_id, entrypoint_id), error) in entrypoint_errors { self.set_global_entrypoint_shortcut_error(plugin_id, entrypoint_id, error)?; } @@ -87,10 +86,15 @@ impl GlobalShortcutSettings { Ok(data) } - pub fn set_global_shortcut(&self, shortcut: Option) -> anyhow::Result<()> { + pub fn set_global_shortcut( + &self, + global_hotkey_manager: &GlobalHotKeyManager, + shortcut: Option, + ) -> anyhow::Result<()> { let mut state = self.state.lock().map_err(|_| anyhow!("lock is poisoned"))?; - let (hotkey, err) = self.assign_global_shortcut(state.current_global_hotkey, shortcut.clone()); + let (hotkey, err) = + self.assign_global_shortcut(global_hotkey_manager, state.current_global_hotkey, shortcut.clone()); state.current_global_hotkey = hotkey; @@ -171,6 +175,7 @@ impl GlobalShortcutSettings { pub fn set_global_entrypoint_shortcut( &self, + global_hotkey_manager: &GlobalHotKeyManager, plugin_id: PluginId, entrypoint_id: EntrypointId, shortcut: Option, @@ -182,7 +187,7 @@ impl GlobalShortcutSettings { .get(&(plugin_id.clone(), entrypoint_id.clone())) .cloned(); - let (hotkey, err) = self.assign_global_shortcut(current_global_hotkey, shortcut.clone()); + let (hotkey, err) = self.assign_global_shortcut(global_hotkey_manager, current_global_hotkey, shortcut.clone()); if let Some(hotkey) = hotkey { state @@ -319,11 +324,12 @@ impl GlobalShortcutSettings { fn assign_global_shortcut( &self, + global_hotkey_manager: &GlobalHotKeyManager, current_hotkey: Option, new_shortcut: Option, ) -> (Option, anyhow::Result<()>) { if let Some(current_hotkey) = current_hotkey { - if let Err(err) = self.global_hotkey_manager.unregister(current_hotkey.clone()) { + if let Err(err) = global_hotkey_manager.unregister(current_hotkey.clone()) { tracing::warn!( "error occurred when unregistering global shortcut {:?}: {:?}", current_hotkey, @@ -334,7 +340,7 @@ impl GlobalShortcutSettings { if let Some(new_shortcut) = new_shortcut { let hotkey = convert_physical_shortcut_to_hotkey(new_shortcut); - match self.global_hotkey_manager.register(hotkey) { + match global_hotkey_manager.register(hotkey) { Ok(()) => (Some(hotkey), Ok(())), Err(err) => (None, Err(anyhow!(err))), } @@ -343,8 +349,12 @@ impl GlobalShortcutSettings { } } - fn setup_shortcuts(&self, global_shortcut: Option) -> (Option, Option) { - let (current_global_hotkey, result) = self.assign_global_shortcut(None, global_shortcut); + fn setup_shortcuts( + &self, + global_hotkey_manager: &GlobalHotKeyManager, + global_shortcut: Option, + ) -> (Option, Option) { + let (current_global_hotkey, result) = self.assign_global_shortcut(global_hotkey_manager, None, global_shortcut); let result = result.map_err(|err| format!("{:#}", err)).err(); @@ -353,6 +363,7 @@ impl GlobalShortcutSettings { fn setup_entrypoint_shortcuts( &self, + global_hotkey_manager: &GlobalHotKeyManager, global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), PhysicalShortcut>, ) -> ( HashMap<(PluginId, EntrypointId), HotKey>, @@ -362,7 +373,7 @@ impl GlobalShortcutSettings { let mut current_entrypoint_global_hotkeys = HashMap::new(); for ((plugin_id, entrypoint_id), shortcut) in global_entrypoint_shortcuts { - let (global_hotkey, result) = self.assign_global_shortcut(None, Some(shortcut)); + let (global_hotkey, result) = self.assign_global_shortcut(global_hotkey_manager, None, Some(shortcut)); if let Some(global_hotkey) = global_hotkey { current_entrypoint_global_hotkeys.insert((plugin_id.clone(), entrypoint_id.clone()), global_hotkey); diff --git a/rust/server/src/plugins/settings/mod.rs b/rust/server/src/plugins/settings/mod.rs index cd95ca0..4fee0a8 100644 --- a/rust/server/src/plugins/settings/mod.rs +++ b/rust/server/src/plugins/settings/mod.rs @@ -14,6 +14,7 @@ use gauntlet_common::model::UiTheme; use gauntlet_common::model::WindowPositionMode; use gauntlet_common::rpc::frontend_api::FrontendApi; use gauntlet_common::rpc::frontend_api::FrontendApiProxy; +use global_hotkey::GlobalHotKeyManager; use crate::plugins::data_db_repository::DataDbRepository; use crate::plugins::data_db_repository::DbSettingsEntrypointSearchAliasData; @@ -45,8 +46,8 @@ impl Settings { }) } - pub fn setup(&self) -> anyhow::Result<()> { - self.global_hotkey_settings.setup()?; + pub fn setup(&self, global_hotkey_manager: &GlobalHotKeyManager) -> anyhow::Result<()> { + self.global_hotkey_settings.setup(global_hotkey_manager)?; Ok(()) } @@ -62,8 +63,13 @@ impl Settings { self.global_hotkey_settings.global_shortcut() } - pub async fn set_global_shortcut(&self, shortcut: Option) -> anyhow::Result<()> { - self.global_hotkey_settings.set_global_shortcut(shortcut) + pub fn set_global_shortcut( + &self, + global_hotkey_manager: &GlobalHotKeyManager, + shortcut: Option, + ) -> anyhow::Result<()> { + self.global_hotkey_settings + .set_global_shortcut(global_hotkey_manager, shortcut) } pub fn global_entrypoint_shortcuts( @@ -72,17 +78,22 @@ impl Settings { self.global_hotkey_settings.global_entrypoint_shortcuts() } - pub async fn set_global_entrypoint_shortcut( + pub fn set_global_entrypoint_shortcut( &self, + global_hotkey_manager: &GlobalHotKeyManager, plugin_id: PluginId, entrypoint_id: EntrypointId, shortcut: Option, ) -> anyhow::Result<()> { - self.global_hotkey_settings - .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) + self.global_hotkey_settings.set_global_entrypoint_shortcut( + global_hotkey_manager, + plugin_id, + entrypoint_id, + shortcut, + ) } - pub async fn entrypoint_search_aliases(&self) -> anyhow::Result> { + pub fn entrypoint_search_aliases(&self) -> anyhow::Result> { let settings = self.repository.get_settings()?; let data: HashMap<_, _> = settings @@ -103,7 +114,7 @@ impl Settings { Ok(data) } - pub async fn set_entrypoint_search_alias( + pub fn set_entrypoint_search_alias( &self, plugin_id: PluginId, entrypoint_id: EntrypointId, @@ -172,7 +183,7 @@ impl Settings { Ok(theme) } - pub async fn theme_setting(&self) -> anyhow::Result { + pub fn theme_setting(&self) -> anyhow::Result { if let Some(_) = read_theme_file(self.dirs.theme_file()) { return Ok(SettingsTheme::ThemeFile); }; diff --git a/rust/server/src/rpc.rs b/rust/server/src/rpc.rs index bafb09f..42fed62 100644 --- a/rust/server/src/rpc.rs +++ b/rust/server/src/rpc.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::sync::Arc; use gauntlet_common::model::DownloadStatus; use gauntlet_common::model::EntrypointId; @@ -13,20 +12,32 @@ use gauntlet_common::model::WindowPositionMode; use gauntlet_common::rpc::backend_api::BackendForCliApi; use gauntlet_common::rpc::backend_api::BackendForSettingsApi; use gauntlet_common::rpc::backend_api::BackendForToolsApi; +use gauntlet_common::rpc::backend_server::start_backend_server; +use gauntlet_common::rpc::server_grpc_api::ServerGrpcApi; +use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiProxy; use gauntlet_utils::channel::RequestResult; -use crate::plugins::ApplicationManager; - pub struct BackendServerImpl { - pub application_manager: Arc, + pub proxy: ServerGrpcApiProxy, } impl BackendServerImpl { - pub fn new(application_manager: Arc) -> Self { - Self { application_manager } + pub fn new(application_manager: ServerGrpcApiProxy) -> Self { + Self { + proxy: application_manager, + } } } +pub async fn run_grpc_server(grpc_api: ServerGrpcApiProxy) { + start_backend_server( + Box::new(BackendServerImpl::new(grpc_api.clone())), + Box::new(BackendServerImpl::new(grpc_api.clone())), + Box::new(BackendServerImpl::new(grpc_api.clone())), + ) + .await +} + #[tonic::async_trait] impl BackendForCliApi for BackendServerImpl { async fn ping(&self) -> RequestResult<()> { @@ -35,11 +46,13 @@ impl BackendForCliApi for BackendServerImpl { } async fn show_window(&self) -> RequestResult<()> { - self.application_manager.show_window().await.map_err(Into::into) + self.proxy.show_window().await?; + + Ok(()) } async fn show_settings_window(&self) -> RequestResult<()> { - self.application_manager.handle_open_settings_window(); + self.proxy.show_settings_window().await?; Ok(()) } @@ -50,9 +63,7 @@ impl BackendForCliApi for BackendServerImpl { entrypoint_id: EntrypointId, action_id: String, ) -> RequestResult<()> { - self.application_manager - .run_action(plugin_id, entrypoint_id, action_id) - .await?; + self.proxy.run_action(plugin_id, entrypoint_id, action_id).await?; Ok(()) } @@ -61,7 +72,7 @@ impl BackendForCliApi for BackendServerImpl { #[tonic::async_trait] impl BackendForToolsApi for BackendServerImpl { async fn save_local_plugin(&self, path: String) -> RequestResult { - let result = self.application_manager.save_local_plugin(&path)?; + let result = self.proxy.save_local_plugin(path).await?; Ok(result) } @@ -70,21 +81,11 @@ impl BackendForToolsApi for BackendServerImpl { #[tonic::async_trait] impl BackendForSettingsApi for BackendServerImpl { async fn plugins(&self) -> RequestResult> { - let result = self.application_manager.plugins(); - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'plugins' request {:?}", - err - ) - } - - result.map_err(Into::into) + self.proxy.plugins().await } async fn set_plugin_state(&self, plugin_id: PluginId, enabled: bool) -> RequestResult<()> { - let result = self.application_manager.set_plugin_state(plugin_id, enabled); + let result = self.proxy.set_plugin_state(plugin_id, enabled).await; if let Err(err) = &result { tracing::warn!( @@ -103,10 +104,7 @@ impl BackendForSettingsApi for BackendServerImpl { entrypoint_id: EntrypointId, enabled: bool, ) -> RequestResult<()> { - let result = self - .application_manager - .set_entrypoint_state(plugin_id, entrypoint_id, enabled) - .await; + let result = self.proxy.set_entrypoint_state(plugin_id, entrypoint_id, enabled).await; if let Err(err) = &result { tracing::warn!( @@ -120,7 +118,7 @@ impl BackendForSettingsApi for BackendServerImpl { } async fn set_global_shortcut(&self, shortcut: Option) -> RequestResult> { - let result = self.application_manager.set_global_shortcut(shortcut).await; + let result = self.proxy.set_global_shortcut(shortcut).await; if let Err(err) = &result { tracing::warn!( @@ -130,13 +128,14 @@ impl BackendForSettingsApi for BackendServerImpl { ) } - Ok(result.err().map(|err| format!("{:#}", err))) + result } async fn get_global_shortcut(&self) -> RequestResult<(Option, Option)> { let result = self - .application_manager - .get_global_shortcut()? + .proxy + .get_global_shortcut() + .await? .map(|(shortcut, error)| (Some(shortcut), error)) .unwrap_or((None, None)); @@ -149,18 +148,26 @@ impl BackendForSettingsApi for BackendServerImpl { entrypoint_id: EntrypointId, shortcut: Option, ) -> RequestResult<()> { - self.application_manager + let result = self + .proxy .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) - .await - .map_err(Into::into) + .await; + + if let Err(err) = &result { + tracing::warn!( + target = "rpc", + "error occurred when handling 'set_global_entrypoint_shortcut' request {:?}", + err + ) + } + + result } async fn get_global_entrypoint_shortcuts( &self, ) -> RequestResult)>> { - self.application_manager - .get_global_entrypoint_shortcut() - .map_err(Into::into) + self.proxy.get_global_entrypoint_shortcuts().await } async fn set_entrypoint_search_alias( @@ -169,36 +176,40 @@ impl BackendForSettingsApi for BackendServerImpl { entrypoint_id: EntrypointId, alias: Option, ) -> RequestResult<()> { - self.application_manager + let result = self + .proxy .set_entrypoint_search_alias(plugin_id, entrypoint_id, alias) - .await - .map_err(Into::into) + .await; + + if let Err(err) = &result { + tracing::warn!( + target = "rpc", + "error occurred when handling 'set_entrypoint_search_alias' request {:?}", + err + ) + } + + result } async fn get_entrypoint_search_aliases(&self) -> RequestResult> { - self.application_manager - .get_entrypoint_search_aliases() - .await - .map_err(Into::into) + self.proxy.get_entrypoint_search_aliases().await } async fn set_theme(&self, theme: SettingsTheme) -> RequestResult<()> { - self.application_manager.set_theme(theme).await.map_err(Into::into) + self.proxy.set_theme(theme).await } async fn get_theme(&self) -> RequestResult { - self.application_manager.get_theme().await.map_err(Into::into) + self.proxy.get_theme().await } async fn set_window_position_mode(&self, mode: WindowPositionMode) -> RequestResult<()> { - self.application_manager - .set_window_position_mode(mode) - .await - .map_err(Into::into) + self.proxy.set_window_position_mode(mode).await } async fn get_window_position_mode(&self) -> RequestResult { - self.application_manager.get_window_position_mode().map_err(Into::into) + self.proxy.get_window_position_mode().await } async fn set_preference_value( @@ -208,9 +219,10 @@ impl BackendForSettingsApi for BackendServerImpl { preference_id: String, preference_value: PluginPreferenceUserData, ) -> RequestResult<()> { - let result = - self.application_manager - .set_preference_value(plugin_id, entrypoint_id, preference_id, preference_value); + let result = self + .proxy + .set_preference_value(plugin_id, entrypoint_id, preference_id, preference_value) + .await; if let Err(err) = &result { tracing::warn!( @@ -224,7 +236,7 @@ impl BackendForSettingsApi for BackendServerImpl { } async fn download_plugin(&self, plugin_id: PluginId) -> RequestResult<()> { - let result = self.application_manager.download_plugin(plugin_id); + let result = self.proxy.download_plugin(plugin_id).await; if let Err(err) = &result { tracing::warn!( @@ -238,11 +250,11 @@ impl BackendForSettingsApi for BackendServerImpl { } async fn download_status(&self) -> RequestResult> { - Ok(self.application_manager.download_status()) + self.proxy.download_status().await } async fn remove_plugin(&self, plugin_id: PluginId) -> RequestResult<()> { - let result = self.application_manager.remove_plugin(plugin_id); + let result = self.proxy.remove_plugin(plugin_id).await; if let Err(err) = &result { tracing::warn!( diff --git a/rust/server/src/search.rs b/rust/server/src/search.rs index 541a7fe..06b24ae 100644 --- a/rust/server/src/search.rs +++ b/rust/server/src/search.rs @@ -184,7 +184,7 @@ impl SearchIndex { Ok(()) } - pub async fn set_entrypoint_search_alias( + pub fn set_entrypoint_search_alias( &self, plugin_id: PluginId, entrypoint_id: EntrypointId, @@ -243,7 +243,7 @@ impl SearchIndex { Ok(()) } - pub async fn save_for_plugin( + pub fn save_for_plugin( &self, plugin_id: PluginId, plugin_name: String, @@ -252,7 +252,7 @@ impl SearchIndex { ) -> anyhow::Result<()> { tracing::debug!("Reloading search index for plugin {:?}", plugin_id); - let aliases = self.settings.entrypoint_search_aliases().await?; + let aliases = self.settings.entrypoint_search_aliases()?; // writer panics if another writer exists let _guard = self.index_writer_mutex.lock().expect("lock is poisoned"); diff --git a/rust/utils_macros/src/boundary_gen.rs b/rust/utils_macros/src/boundary_gen.rs index 7de3e6c..1d3a99a 100644 --- a/rust/utils_macros/src/boundary_gen.rs +++ b/rust/utils_macros/src/boundary_gen.rs @@ -51,6 +51,10 @@ pub fn boundary_gen(args: TokenStream, input: TokenStream) -> TokenStream { ); let proxy_struct_name = syn::Ident::new(&format!("{}Proxy", ident.to_string()), proc_macro2::Span::call_site()); + let proxy_handler_name = syn::Ident::new( + &format!("handle_proxy_message_{}", ident.to_string().to_case(Case::Snake)), + proc_macro2::Span::call_site(), + ); let request_enum_items: Vec<_> = items .into_iter() @@ -197,7 +201,7 @@ pub fn boundary_gen(args: TokenStream, input: TokenStream) -> TokenStream { #(#impl_fns)* } - pub async fn handle_proxy_message(message: #request_enum_name, api: &impl #ident) -> anyhow::Result<#response_enum_name> { + pub async fn #proxy_handler_name(message: #request_enum_name, api: &impl #ident) -> anyhow::Result<#response_enum_name> { match message { #(#handle_impl_fns)* } From 75fa1f3af520f8b151f75be3b8348eafa8df2899 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 19 Jun 2025 11:18:51 +0200 Subject: [PATCH 33/91] Bump a lot of rust dependencies --- Cargo.lock | 637 +++++++++++++++++---------------- rust/plugin_runtime/Cargo.toml | 8 +- 2 files changed, 336 insertions(+), 309 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a31edda..439b9d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adler32" @@ -148,9 +148,12 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] name = "aligned-vec" -version = "0.5.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] [[package]] name = "alloc-no-stdlib" @@ -217,9 +220,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -232,33 +235,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.8" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", "once_cell_polyfill", @@ -315,7 +318,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -420,7 +423,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common 1.0.0", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -460,9 +463,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.23" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" +checksum = "40f6024f3f856663b45fd0c9b6f2024034a702f453549449e0d84a305900dad4" dependencies = [ "brotli 8.0.1", "flate2", @@ -586,7 +589,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -652,7 +655,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -669,7 +672,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -727,9 +730,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "av1-grain" @@ -816,7 +819,7 @@ dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide 0.8.8", + "miniz_oxide 0.8.9", "object", "rustc-demangle", "windows-targets 0.52.6", @@ -858,9 +861,9 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "basic-toml" @@ -873,9 +876,9 @@ dependencies = [ [[package]] name = "better_scoped_tls" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50fd297a11c709be8348aec039c8b91de16075d2b2bdaee1bd562c0875993664" +checksum = "7cd228125315b132eed175bf47619ac79b945b26e56b848ba203ae4ea8603609" dependencies = [ "scoped-tls", ] @@ -926,7 +929,7 @@ dependencies = [ "regex", "rustc-hash 2.1.1", "shlex", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1063,7 +1066,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1126,18 +1129,18 @@ checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" dependencies = [ "allocator-api2", ] [[package]] name = "bytemuck" -version = "1.23.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" dependencies = [ "bytemuck_derive", ] @@ -1150,7 +1153,7 @@ checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1299,7 +1302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1345,9 +1348,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.24" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "jobserver", "libc", @@ -1387,9 +1390,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -1433,9 +1436,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -1443,9 +1446,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -1455,21 +1458,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clipboard-win" @@ -1538,7 +1541,7 @@ dependencies = [ "nom 7.1.3", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1549,9 +1552,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "combine" @@ -2035,7 +2038,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -2089,7 +2092,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -2100,7 +2103,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -2192,7 +2195,7 @@ dependencies = [ "swc_visit_macros", "text_lines", "thiserror 2.0.12", - "unicode-width 0.2.0", + "unicode-width 0.2.1", "url", ] @@ -2442,7 +2445,7 @@ checksum = "5abb2556e91848b66f562451fcbcdee2a3b7c88281828908dcf7cca355f5d997" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -2851,7 +2854,7 @@ dependencies = [ "stringcase", "strum 0.27.1", "strum_macros 0.27.1", - "syn 2.0.101", + "syn 2.0.103", "thiserror 2.0.12", ] @@ -3125,7 +3128,7 @@ dependencies = [ "deno_core", "deno_error", "deno_native_certs", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-pemfile", "rustls-tokio-stream", "rustls-webpki 0.102.8", @@ -3137,11 +3140,11 @@ dependencies = [ [[package]] name = "deno_unsync" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c618b51088b3ac67f15c69b3ed7620ba3a7d495e5a090186df9424b5ab623e" +checksum = "6742a724e8becb372a74c650a1aefb8924a5b8107f7d75b3848763ea24b27a87" dependencies = [ - "futures", + "futures-util", "parking_lot", "tokio", ] @@ -3367,7 +3370,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3398,7 +3401,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3408,7 +3411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3497,7 +3500,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3524,7 +3527,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3562,13 +3565,13 @@ dependencies = [ [[package]] name = "dlopen2_derive" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3895,14 +3898,14 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "enumflags2" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", @@ -3910,13 +3913,13 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3925,6 +3928,26 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -4018,7 +4041,7 @@ dependencies = [ "bit_field", "half", "lebe", - "miniz_oxide 0.8.8", + "miniz_oxide 0.8.9", "rayon-core", "smallvec", "zune-inflate", @@ -4165,12 +4188,12 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", - "miniz_oxide 0.8.8", + "miniz_oxide 0.8.9", ] [[package]] @@ -4273,7 +4296,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4334,7 +4357,7 @@ checksum = "8d7ccf961415e7aa17ef93dcb6c2441faaa8e768abe09e659b908089546f74c5" dependencies = [ "proc-macro2", "swc_macros_common 1.0.0", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4453,7 +4476,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4622,7 +4645,7 @@ name = "gauntlet-manifest-schema" version = "0.0.0" dependencies = [ "gauntlet-server", - "schemars", + "schemars 0.8.22", "serde_json", ] @@ -4717,7 +4740,7 @@ dependencies = [ "regex", "rusqlite", "rusqlite_migration", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "tantivy", @@ -4752,7 +4775,7 @@ dependencies = [ "convert_case 0.8.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4824,7 +4847,7 @@ dependencies = [ "libc", "log", "rustversion", - "windows 0.61.1", + "windows 0.61.3", ] [[package]] @@ -4857,7 +4880,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -5069,7 +5092,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -5201,7 +5224,7 @@ checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ "bitflags 2.9.1", "gpu-descriptor-types", - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -5273,7 +5296,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -5383,9 +5406,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", @@ -5398,7 +5421,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -5442,15 +5465,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -5552,9 +5569,9 @@ dependencies = [ [[package]] name = "hstr" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71399f53a92ef72ee336a4b30201c6e944827e14e0af23204c291aad9c24cc85" +checksum = "1210512c4d06880c08a940a378a102ca860735b628f88d5aef91892b941c9235" dependencies = [ "hashbrown 0.14.5", "new_debug_unreachable", @@ -5694,14 +5711,14 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.6" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", "hyper 1.6.0", "hyper-util", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-pki-types", "tokio", "tokio-rustls", @@ -5723,9 +5740,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" dependencies = [ "bytes", "futures-channel", @@ -5849,7 +5866,7 @@ source = "git+https://github.com/project-gauntlet/iced_fonts.git?branch=gauntlet dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "ttf-parser 0.25.1", ] @@ -5921,7 +5938,7 @@ dependencies = [ "manyhow", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6197,9 +6214,9 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b" dependencies = [ "byteorder-lite", "quick-error", @@ -6277,7 +6294,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "serde", ] @@ -6331,7 +6348,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6394,7 +6411,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6694,7 +6711,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6767,9 +6784,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libffi" @@ -6831,7 +6848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.0", + "windows-targets 0.53.2", ] [[package]] @@ -6848,7 +6865,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.9.1", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.13", ] [[package]] @@ -6930,9 +6947,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -6975,7 +6992,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -6986,9 +7003,9 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "lz4_flex" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a" [[package]] name = "malloc_buf" @@ -7009,7 +7026,7 @@ dependencies = [ "manyhow-macros", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -7079,9 +7096,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memmap2" @@ -7167,9 +7184,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", "simd-adler32", @@ -7183,7 +7200,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -7194,7 +7211,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -7292,7 +7309,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -7532,7 +7549,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -7588,11 +7605,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] @@ -7614,7 +7631,7 @@ dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8027,9 +8044,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.108" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -8191,11 +8208,12 @@ dependencies = [ [[package]] name = "os_info" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fc863e2ca13dc2d5c34fb22ea4a588248ac14db929616ba65c45f21744b1e9" +checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" dependencies = [ "log", + "plist", "serde", "windows-sys 0.52.0", ] @@ -8231,7 +8249,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8366,9 +8384,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -8376,13 +8394,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.13", "smallvec", "windows-targets 0.52.6", ] @@ -8491,7 +8509,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8526,7 +8544,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8598,13 +8616,13 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" +checksum = "3d77244ce2d584cd84f6a15f86195b8c9b2a0dfbfd817c09e0464244091a58ed" dependencies = [ "base64 0.22.1", "indexmap 2.9.0", - "quick-xml 0.32.0", + "quick-xml 0.37.5", "serde", "time", ] @@ -8636,7 +8654,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8661,7 +8679,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.8.8", + "miniz_oxide 0.8.9", ] [[package]] @@ -8672,7 +8690,7 @@ checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.5.1", + "hermit-abi", "pin-project-lite", "rustix 1.0.7", "tracing", @@ -8693,9 +8711,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" @@ -8747,12 +8765,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.32" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" dependencies = [ "proc-macro2", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8789,7 +8807,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit 0.22.26", + "toml_edit 0.22.27", ] [[package]] @@ -8824,7 +8842,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8836,7 +8854,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8867,7 +8885,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "version_check", "yansi", ] @@ -8888,7 +8906,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8917,7 +8935,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.101", + "syn 2.0.103", "tempfile", ] @@ -8931,7 +8949,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8969,7 +8987,7 @@ checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -8996,15 +9014,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "quick-xml" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" -dependencies = [ - "memchr", -] - [[package]] name = "quick-xml" version = "0.37.5" @@ -9026,7 +9035,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.1.1", - "rustls 0.23.27", + "rustls 0.23.28", "socket2", "thiserror 2.0.12", "tokio", @@ -9046,7 +9055,7 @@ dependencies = [ "rand 0.9.1", "ring", "rustc-hash 2.1.1", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-pki-types", "slab", "thiserror 2.0.12", @@ -9057,9 +9066,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" dependencies = [ "cfg_aliases", "libc", @@ -9080,9 +9089,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radium" @@ -9284,9 +9293,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags 2.9.1", ] @@ -9330,7 +9339,7 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -9341,7 +9350,7 @@ checksum = "dc06e6b318142614e4a48bc725abbf08ff166694835c43c9dae5a9009704639a" dependencies = [ "allocator-api2", "bumpalo", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "log", "rustc-hash 2.1.1", "smallvec", @@ -9495,7 +9504,7 @@ dependencies = [ "rinja_parser", "rustc-hash 2.1.1", "serde", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -9602,7 +9611,7 @@ dependencies = [ "quote", "rust-embed-utils", "shellexpand", - "syn 2.0.101", + "syn 2.0.103", "walkdir", ] @@ -9648,9 +9657,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -9733,9 +9742,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" dependencies = [ "log", "once_cell", @@ -9785,7 +9794,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faa7dc7c991d9164e55bbf1558029eb5b84d32cc4d61a7df5b8641b2deedc4b3" dependencies = [ "futures", - "rustls 0.23.27", + "rustls 0.23.28", "socket2", "tokio", ] @@ -9924,6 +9933,18 @@ dependencies = [ "serde_json", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "schemars_derive" version = "0.8.22" @@ -9933,7 +9954,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -10077,7 +10098,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -10088,7 +10109,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -10112,14 +10133,14 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -10140,15 +10161,16 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", "indexmap 2.9.0", + "schemars 0.9.0", "serde", "serde_derive", "serde_json", @@ -10158,14 +10180,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -10341,12 +10363,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "slotmap" @@ -10368,9 +10387,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smartstring" @@ -10461,7 +10480,7 @@ dependencies = [ "objc2-foundation 0.2.2", "objc2-quartz-core", "raw-window-handle", - "redox_syscall 0.5.12", + "redox_syscall 0.5.13", "rustix 0.38.44", "tiny-xlib", "wasm-bindgen", @@ -10475,9 +10494,9 @@ dependencies = [ [[package]] name = "sourcemap" -version = "9.2.1" +version = "9.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdee719193ae5c919a3ee43f64c2c0dd87f9b9a451d67918a2a5ec2e3c70561c" +checksum = "e22afbcb92ce02d23815b9795523c005cb9d3c214f8b7a66318541c240ea7935" dependencies = [ "base64-simd", "bitvec", @@ -10571,7 +10590,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common 1.0.0", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -10614,7 +10633,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -10627,7 +10646,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -10739,7 +10758,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common 1.0.0", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -10795,7 +10814,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common 1.0.0", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -10911,7 +10930,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common 1.0.0", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -11023,7 +11042,7 @@ checksum = "e96e15288bf385ab85eb83cff7f9e2d834348da58d0a31b33bdb572e66ee413e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -11034,7 +11053,7 @@ checksum = "27e18fbfe83811ffae2bb23727e45829a0d19c6870bced7c0f545cc99ad248dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -11045,7 +11064,7 @@ checksum = "a509f56fca05b39ba6c15f3e58636c3924c78347d63853632ed2ffcb6f5a0ac7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -11068,7 +11087,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common 0.3.14", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -11084,9 +11103,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" dependencies = [ "proc-macro2", "quote", @@ -11119,7 +11138,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -11377,7 +11396,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -11388,17 +11407,16 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -11554,7 +11572,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -11575,7 +11593,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.27", + "rustls 0.23.28", "tokio", ] @@ -11613,7 +11631,7 @@ dependencies = [ "futures-io", "futures-sink", "futures-util", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "pin-project-lite", "slab", "tokio", @@ -11634,21 +11652,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.26", + "toml_edit 0.22.27", ] [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] @@ -11677,23 +11695,23 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", "toml_write", - "winnow 0.7.10", + "winnow 0.7.11", ] [[package]] name = "toml_write" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "tonic" @@ -11736,7 +11754,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -11776,9 +11794,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "async-compression", "bitflags 2.9.1", @@ -11820,20 +11838,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -12103,9 +12121,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "unicode-xid" @@ -12145,7 +12163,7 @@ dependencies = [ "flate2", "log", "once_cell", - "rustls 0.23.27", + "rustls 0.23.28", "rustls-pki-types", "url", "webpki-roots 0.26.11", @@ -12262,16 +12280,16 @@ dependencies = [ [[package]] name = "v8" -version = "137.1.0" +version = "137.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be127b878f582d3b7602bd2e26a39f90a95b388deb940da7f6554617aedcdf40" +checksum = "d2b387c1c5731284e756c03280032068e68e5b52f6c4714492403c30f650ad52" dependencies = [ "bindgen", "bitflags 2.9.1", "fslock", "gzip-header", "home", - "miniz_oxide 0.8.8", + "miniz_oxide 0.8.9", "paste", "which 6.0.3", ] @@ -12293,9 +12311,9 @@ dependencies = [ [[package]] name = "v_frame" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" dependencies = [ "aligned-vec", "num-traits", @@ -12439,9 +12457,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -12480,7 +12498,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "wasm-bindgen-shared", ] @@ -12515,7 +12533,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -12541,9 +12559,9 @@ dependencies = [ [[package]] name = "wasmtimer" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23" +checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" dependencies = [ "futures", "js-sys", @@ -12921,7 +12939,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" dependencies = [ - "redox_syscall 0.5.12", + "redox_syscall 0.5.13", "wasite", "web-sys", ] @@ -12989,9 +13007,9 @@ dependencies = [ [[package]] name = "windows" -version = "0.61.1" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", "windows-core 0.61.2", @@ -13054,7 +13072,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -13065,7 +13083,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -13076,7 +13094,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -13087,14 +13105,14 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-numerics" @@ -13179,6 +13197,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -13227,9 +13254,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", @@ -13492,9 +13519,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] @@ -13755,7 +13782,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "synstructure 0.13.2", ] @@ -13767,7 +13794,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "synstructure 0.13.2", ] @@ -13836,7 +13863,7 @@ dependencies = [ "tracing", "uds_windows", "windows-sys 0.59.0", - "winnow 0.7.10", + "winnow 0.7.11", "zbus_macros 5.7.1", "zbus_names 4.2.0", "zvariant 5.5.3", @@ -13851,7 +13878,7 @@ dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "zvariant_utils 2.1.0", ] @@ -13864,7 +13891,7 @@ dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "zbus_names 4.2.0", "zvariant 5.5.3", "zvariant_utils 3.2.0", @@ -13889,7 +13916,7 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow 0.7.10", + "winnow 0.7.11", "zvariant 5.5.3", ] @@ -13901,22 +13928,22 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -13936,7 +13963,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "synstructure 0.13.2", ] @@ -13957,7 +13984,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -13990,7 +14017,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -14038,9 +14065,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +checksum = "0f6fe2e33d02a98ee64423802e16df3de99c43e5cf5ff983767e1128b394c8ac" dependencies = [ "zune-core", ] @@ -14068,7 +14095,7 @@ dependencies = [ "enumflags2", "serde", "url", - "winnow 0.7.10", + "winnow 0.7.11", "zvariant_derive 5.5.3", "zvariant_utils 3.2.0", ] @@ -14082,7 +14109,7 @@ dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "zvariant_utils 2.1.0", ] @@ -14095,7 +14122,7 @@ dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "zvariant_utils 3.2.0", ] @@ -14107,7 +14134,7 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -14120,6 +14147,6 @@ dependencies = [ "quote", "serde", "static_assertions", - "syn 2.0.101", - "winnow 0.7.10", + "syn 2.0.103", + "winnow 0.7.11", ] diff --git a/rust/plugin_runtime/Cargo.toml b/rust/plugin_runtime/Cargo.toml index 8e166d6..7b429b6 100644 --- a/rust/plugin_runtime/Cargo.toml +++ b/rust/plugin_runtime/Cargo.toml @@ -29,10 +29,10 @@ typed-path.workspace = true interprocess.workspace = true # deno crates -deno_core = { version = "0.347.0" } # https://github.com/denoland/deno/blob/v2.3.3 -deno_runtime = { version = "0.213.0", features = ["transpile"] } -deno_error = "*" -deno_resolver = "*" +deno_core = { version = "=0.347.0" } # https://github.com/denoland/deno/blob/v2.3.3 +deno_runtime = { version = "=0.213.0", features = ["transpile"] } +deno_error = "=0.5.6" +deno_resolver = "=0.36.0" sys_traits = "*" # other From 29b1a4ab362b649fe4e9b9bcc53edb890a518e30 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 19 Jun 2025 11:48:53 +0200 Subject: [PATCH 34/91] Fix useStorage and useCache hooks crashing plugin runtime when closing the view --- js/api/src/hooks.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/js/api/src/hooks.ts b/js/api/src/hooks.ts index 5d4527c..e452ca4 100644 --- a/js/api/src/hooks.ts +++ b/js/api/src/hooks.ts @@ -302,7 +302,8 @@ function useWebStorage( useEffect(() => { if (value === undefined) { - return storageObject.removeItem(key) + storageObject.removeItem(key) + return } storageObject.setItem(key, JSON.stringify(value)) }, [key, value, storageObject]) @@ -396,4 +397,4 @@ export function useFetch( onWillExecute: options?.onWillExecute, } ) -} \ No newline at end of file +} From 2afecf3b80c833b3fb59a4faac3e5e34cd64cb0d Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 19 Jun 2025 12:05:10 +0200 Subject: [PATCH 35/91] Fix padding on grid component after dependency updates --- rust/client/src/ui/theme/container.rs | 2 ++ rust/client/src/ui/theme/mod.rs | 6 +++++- rust/client/src/ui/widget/grid.rs | 17 ++++++++++++----- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/rust/client/src/ui/theme/container.rs b/rust/client/src/ui/theme/container.rs index cbcb5ec..359dd88 100644 --- a/rust/client/src/ui/theme/container.rs +++ b/rust/client/src/ui/theme/container.rs @@ -61,6 +61,7 @@ pub enum ContainerStyle { RootTopPanel, Grid, GridInner, + GridSection, List, ListInner, TextAccessory, @@ -377,6 +378,7 @@ impl<'a, Message: 'a> ThemableWidget<'a, Message> for Container<'a, Message, Gau ContainerStyle::FormInner => self.padding(theme.form_inner.padding.to_iced()), ContainerStyle::GridInner => self.padding(theme.grid_inner.padding.to_iced()), ContainerStyle::Grid => self.padding(theme.grid.padding.to_iced()), + ContainerStyle::GridSection => self.padding(theme.grid_section.padding.to_iced()), ContainerStyle::List => self.padding(theme.list.padding.to_iced()), ContainerStyle::ListInner => self.padding(theme.list_inner.padding.to_iced()), ContainerStyle::RootBottomPanelActionToggleText => { diff --git a/rust/client/src/ui/theme/mod.rs b/rust/client/src/ui/theme/mod.rs index eb566d3..9bc9fcc 100644 --- a/rust/client/src/ui/theme/mod.rs +++ b/rust/client/src/ui/theme/mod.rs @@ -65,6 +65,7 @@ pub struct GauntletComplexTheme { grid_item: ThemeButton, grid_item_title: ThemePaddingTextColor, grid_item_subtitle: ThemeTextColor, + grid_section: ThemePaddingOnly, grid_section_title: ThemePaddingTextColorSpacing, grid_section_subtitle: ThemeTextColor, inline: ThemePaddingOnly, @@ -438,8 +439,11 @@ impl GauntletComplexTheme { spacing: 8.0, }, list_section_subtitle: ThemeTextColor { text_color: text_300 }, + grid_section: ThemePaddingOnly { + padding: padding(0.0, 0.0, 16.0, 0.0), + }, grid_section_title: ThemePaddingTextColorSpacing { - padding: padding(12.0, 0.0, 4.0, 0.0), + padding: padding(4.0, 0.0, 4.0, 0.0), text_color: text_200, spacing: 8.0, }, diff --git a/rust/client/src/ui/widget/grid.rs b/rust/client/src/ui/widget/grid.rs index 1ac3361..c12c8c2 100644 --- a/rust/client/src/ui/widget/grid.rs +++ b/rust/client/src/ui/widget/grid.rs @@ -64,8 +64,12 @@ impl<'b> ComponentWidgets<'b> { } GridWidgetOrderedMembers::GridSection(widget) => { if !pending.is_empty() { - let content = - self.render_grid(&pending, &grid_widget.columns, focused_item.index, index_counter); + let content = self.render_grid_section( + &pending, + &grid_widget.columns, + focused_item.index, + index_counter, + ); items.push(content); @@ -85,7 +89,8 @@ impl<'b> ComponentWidgets<'b> { } if !pending.is_empty() { - let content = self.render_grid(&pending, &grid_widget.columns, focused_item.index, index_counter); + let content = + self.render_grid_section(&pending, &grid_widget.columns, focused_item.index, index_counter); items.push(content); } @@ -138,7 +143,7 @@ impl<'b> ComponentWidgets<'b> { }) .collect(); - let content = self.render_grid(&items, &widget.columns, item_focus_index, index_counter); + let content = self.render_grid_section(&items, &widget.columns, item_focus_index, index_counter); let section_title_style = if first_section { RowStyle::GridFirstSectionTitle @@ -240,7 +245,7 @@ impl<'b> ComponentWidgets<'b> { content } - fn render_grid<'a>( + fn render_grid_section<'a>( &self, items: &[&GridItemWidget], /*aspect_ratio: Option<&str>,*/ @@ -273,6 +278,8 @@ impl<'b> ComponentWidgets<'b> { let grid = grid(rows).columns(columns).themed(GridStyle::Default); + let grid = container(grid).themed(ContainerStyle::GridSection); + grid } } From 0d7a9db4d9ab5eb6cc0927ff4ba294b74cde55a8 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:21:15 +0200 Subject: [PATCH 36/91] Fix icons font not being loaded in main window --- rust/client/src/ui/mod.rs | 8 ++------ rust/common_ui/src/lib.rs | 2 -- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 0ddacba..a3bdc98 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -218,7 +218,6 @@ pub enum AppMsg { widget_event: ComponentWidgetEvent, }, Noop, - FontLoaded(Result<(), font::Error>), ShowWindow, HideWindow, ToggleWindow, @@ -482,6 +481,7 @@ fn run_non_wayland(minimized: bool) -> anyhow::Result<()> { }, ..Default::default() }) + .font(BOOTSTRAP_FONT_BYTES) .subscription(subscription) .theme(|state, _| state.theme.clone()) .run()?; @@ -529,7 +529,7 @@ fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, let theme = GauntletComplexTheme::new(setup_data.theme); GauntletComplexTheme::set_global(theme.clone()); - let mut tasks = vec![font::load(BOOTSTRAP_FONT_BYTES).map(AppMsg::FontLoaded)]; + let mut tasks = vec![]; #[cfg(target_os = "linux")] let (main_window_id, open_task) = if wayland { @@ -1145,10 +1145,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { render_location, } => state.handle_plugin_event(widget_event, plugin_id, render_location), AppMsg::Noop => Task::none(), - AppMsg::FontLoaded(result) => { - result.expect("unable to load font"); - Task::none() - } AppMsg::ToggleWindow => state.toggle_window(), AppMsg::ShowWindow => state.show_window(), AppMsg::HideWindow => state.hide_window(true), diff --git a/rust/common_ui/src/lib.rs b/rust/common_ui/src/lib.rs index 705dde3..a3329d9 100644 --- a/rust/common_ui/src/lib.rs +++ b/rust/common_ui/src/lib.rs @@ -6,7 +6,6 @@ use iced::Pixels; use iced::border::Radius; use iced::keyboard::Modifiers; use iced::widget::text; -use iced_fonts::BOOTSTRAP_FONT; use iced_fonts::bootstrap::arrow_return_left; use iced_fonts::bootstrap::command; use iced_fonts::bootstrap::option; @@ -96,7 +95,6 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( if cfg!(target_os = "macos") { Some( text("^") // TODO bootstrap doesn't have proper macos ctrl icon - .font(BOOTSTRAP_FONT) // todo replace .into(), ) } else { From f51a1234119c18a1f646cea5fe4874c3be50d3a1 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:28:19 +0200 Subject: [PATCH 37/91] Change layershell namespace from `Gauntlet` to `gauntlet`, set hud to `gauntlet-hud` --- rust/client/src/ui/hud/mod.rs | 2 +- rust/client/src/ui/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/client/src/ui/hud/mod.rs b/rust/client/src/ui/hud/mod.rs index a978ffe..771c181 100644 --- a/rust/client/src/ui/hud/mod.rs +++ b/rust/client/src/ui/hud/mod.rs @@ -83,7 +83,7 @@ fn layer_shell_settings() -> iced_layershell::reexport::NewLayerShellSettings { margin: Default::default(), exclusive_zone: Some(0), size: Some((HUD_WINDOW_WIDTH as u32, HUD_WINDOW_HEIGHT as u32)), - namespace: None, + namespace: Some("gauntlet-hud".to_string()), } } diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index a3bdc98..ac87371 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -493,7 +493,7 @@ fn run_non_wayland(minimized: bool) -> anyhow::Result<()> { fn run_wayland(minimized: bool) -> anyhow::Result<()> { let boot = move || new(true, minimized); - iced_layershell::build_pattern::daemon(boot, "Gauntlet", update, view) + iced_layershell::build_pattern::daemon(boot, "gauntlet", update, view) .layer_settings(iced_layershell::settings::LayerShellSettings { start_mode: iced_layershell::settings::StartMode::Background, events_transparent: true, From 71e8adf44d4fe47c6bbfb6fb53b7a09b0e2e67cb Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:32:52 +0200 Subject: [PATCH 38/91] Pin ubuntu version in github actions to lower glibc requirement from 2.38 to 2.35 --- .github/workflows/format.yaml | 4 ++-- .github/workflows/nix.yaml | 2 +- .github/workflows/release.yaml | 2 +- .github/workflows/setup-linux.yaml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/format.yaml b/.github/workflows/format.yaml index 9e88d52..e9d96ac 100644 --- a/.github/workflows/format.yaml +++ b/.github/workflows/format.yaml @@ -2,7 +2,7 @@ name: format on: [push, pull_request] jobs: rust: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: dtolnay/rust-toolchain@nightly with: @@ -11,7 +11,7 @@ jobs: - name: rustfmt run: cargo +nightly fmt --all -- --check nix: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: cachix/install-nix-action@v31 - uses: actions/checkout@v4 diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml index 9793897..41a5ab8 100644 --- a/.github/workflows/nix.yaml +++ b/.github/workflows/nix.yaml @@ -2,7 +2,7 @@ name: nix build on: [push, pull_request] jobs: all: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - uses: cachix/install-nix-action@v25 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a75c4f9..b908321 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -25,7 +25,7 @@ on: jobs: publish-init: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 outputs: github-release-id: ${{ steps.init-step.outputs.github-release-id }} steps: diff --git a/.github/workflows/setup-linux.yaml b/.github/workflows/setup-linux.yaml index 1da039b..1a104cf 100644 --- a/.github/workflows/setup-linux.yaml +++ b/.github/workflows/setup-linux.yaml @@ -14,7 +14,7 @@ on: jobs: run-on-linux: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 timeout-minutes: 60 steps: - run: sudo apt-get update From f20537181ea6bdd0978baf41557e402870fa969c Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 19 Jun 2025 15:03:41 +0200 Subject: [PATCH 39/91] Bump npm dependencies --- js/core/typings/index.d.ts | 22 - js/typings/index.d.ts | 11 + package-lock.json | 933 ++++++++++++++++--------------------- 3 files changed, 405 insertions(+), 561 deletions(-) delete mode 100644 js/core/typings/index.d.ts diff --git a/js/core/typings/index.d.ts b/js/core/typings/index.d.ts deleted file mode 100644 index 44d9679..0000000 --- a/js/core/typings/index.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -declare module "ext:gauntlet/renderer.js" { - import { ReactNode } from "react"; - - export const render: (entrypointId: string, entrypointName: string, renderLocation: RenderLocation, component: ReactNode) => UiWidget; - export const clearRenderer: () => void; -} - -declare module "gauntlet:core" { - export function runPluginLoop(): Promise; -} - -declare module "ext:gauntlet/api/components.js" { - -} - -declare module "ext:gauntlet/api/hooks.js" { - -} - -declare module "ext:gauntlet/api/helpers.js" { - -} diff --git a/js/typings/index.d.ts b/js/typings/index.d.ts index 66c0542..d1de71d 100644 --- a/js/typings/index.d.ts +++ b/js/typings/index.d.ts @@ -142,6 +142,10 @@ type GeneratedSearchItemAction = { label: string, } +declare module "gauntlet:core" { + export function runPluginLoop(): Promise; +} + declare module "gauntlet:bridge/internal-all" { function open_settings(): void function run_numbat(input: string): { left: string, right: string } @@ -180,6 +184,13 @@ declare module "gauntlet:bridge/internal-windows" { function windows_app_from_path(path: string): Promise> } +declare module "ext:gauntlet/renderer.js" { + import { ReactNode } from "react"; + + export const render: (entrypointId: string, entrypointName: string, renderLocation: RenderLocation, component: ReactNode) => UiWidget; + export const clearRenderer: () => void; +} + declare module "ext:core/ops" { function open_settings(): void function run_numbat(input: string): { left: string, right: string } diff --git a/package-lock.json b/package-lock.json index 6c456b2..74b0da5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -81,97 +81,6 @@ "resolved": "example_plugins/js/api", "link": true }, - "example_plugins/plugins/docs_detail": { - "name": "@project-gauntlet/docs-detailt", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "example_plugins/plugins/docs_form": { - "name": "@project-gauntlet/docs-form", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "example_plugins/plugins/docs_grid": { - "name": "@project-gauntlet/docs-grid", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "example_plugins/plugins/docs_inline_separators": { - "name": "@project-gauntlet/docs-inline-separators", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "example_plugins/plugins/docs_inline_three_sections": { - "name": "@project-gauntlet/docs-inline-three-sections", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "example_plugins/plugins/docs_inline_two_sections": { - "name": "@project-gauntlet/docs-inline-two-sections", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "example_plugins/plugins/docs_list": { - "name": "@project-gauntlet/docs-list", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, "example_plugins/plugins/entrypoint_generator_accessories": { "name": "@project-gauntlet/docs-entrypoint-generator-accessories", "dependencies": { @@ -764,9 +673,9 @@ } }, "node_modules/@grpc/grpc-js": { - "version": "1.12.5", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", - "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz", + "integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -778,9 +687,9 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.7.13", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", - "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -796,6 +705,29 @@ "node": ">=6" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -833,9 +765,9 @@ } }, "node_modules/@jsr/std__path": { - "version": "1.0.8", - "resolved": "https://npm.jsr.io/~/11/@jsr/std__path/1.0.8.tgz", - "integrity": "sha512-eNBGlh/8ZVkMxtFH4bwIzlAeKoHYk5in4wrBZhi20zMdOiuX4QozP4+19mIXBT2lzHDjhuVLyECbhFeR304iDg==" + "version": "1.1.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__path/1.1.0.tgz", + "integrity": "sha512-rnxGg/nJGfDbJO+xIJ9YEzLD7dCzjr3NHShf4dbnlt44WEYNwMjg+TcDO6F2NPHBnn/6iUFwbnNzysrZvyD1Og==" }, "node_modules/@kwsites/file-exists": { "version": "1.1.1", @@ -863,34 +795,34 @@ } }, "node_modules/@octokit/app": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/@octokit/app/-/app-15.1.1.tgz", - "integrity": "sha512-fk8xrCSPTJGpyBdBNI+DcZ224dm0aApv4vi6X7/zTmANXlegKV2Td+dJ+fd7APPaPN7R+xttUsj2Fm+AFDSfMQ==", + "version": "15.1.6", + "resolved": "https://registry.npmjs.org/@octokit/app/-/app-15.1.6.tgz", + "integrity": "sha512-WELCamoCJo9SN0lf3SWZccf68CF0sBNPQuLYmZ/n87p5qvBJDe9aBtr5dHkh7T9nxWZ608pizwsUbypSzZAiUw==", "license": "MIT", "dependencies": { - "@octokit/auth-app": "^7.0.0", - "@octokit/auth-unauthenticated": "^6.0.0", - "@octokit/core": "^6.1.2", - "@octokit/oauth-app": "^7.0.0", - "@octokit/plugin-paginate-rest": "^11.0.0", - "@octokit/types": "^13.0.0", - "@octokit/webhooks": "^13.0.0" + "@octokit/auth-app": "^7.2.1", + "@octokit/auth-unauthenticated": "^6.1.3", + "@octokit/core": "^6.1.5", + "@octokit/oauth-app": "^7.1.6", + "@octokit/plugin-paginate-rest": "^12.0.0", + "@octokit/types": "^14.0.0", + "@octokit/webhooks": "^13.6.1" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/auth-app": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-7.1.3.tgz", - "integrity": "sha512-GZdkOp2kZTIy5dG9oXqvzUAZiPvDx4C/lMlN6yQjtG9d/+hYa7W8WXTJoOrXE8UdfL9A/sZMl206dmtkl9lwVQ==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-7.2.1.tgz", + "integrity": "sha512-4jaopCVOtWN0V8qCx/1s2pkRqC6tcvIQM3kFB99eIpsP53GfsoIKO08D94b83n/V3iGihHmxWR2lXzE0NicUGg==", "license": "MIT", "dependencies": { - "@octokit/auth-oauth-app": "^8.1.0", - "@octokit/auth-oauth-user": "^5.1.0", - "@octokit/request": "^9.1.1", - "@octokit/request-error": "^6.1.1", - "@octokit/types": "^13.4.1", + "@octokit/auth-oauth-app": "^8.1.4", + "@octokit/auth-oauth-user": "^5.1.4", + "@octokit/request": "^9.2.3", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", "toad-cache": "^3.7.0", "universal-github-app-jwt": "^2.2.0", "universal-user-agent": "^7.0.0" @@ -900,15 +832,15 @@ } }, "node_modules/@octokit/auth-oauth-app": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-8.1.1.tgz", - "integrity": "sha512-5UtmxXAvU2wfcHIPPDWzVSAWXVJzG3NWsxb7zCFplCWEmMCArSZV0UQu5jw5goLQXbFyOr5onzEH37UJB3zQQg==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-8.1.4.tgz", + "integrity": "sha512-71iBa5SflSXcclk/OL3lJzdt4iFs56OJdpBGEBl1wULp7C58uiswZLV6TdRaiAzHP1LT8ezpbHlKuxADb+4NkQ==", "license": "MIT", "dependencies": { - "@octokit/auth-oauth-device": "^7.0.0", - "@octokit/auth-oauth-user": "^5.0.1", - "@octokit/request": "^9.0.0", - "@octokit/types": "^13.0.0", + "@octokit/auth-oauth-device": "^7.1.5", + "@octokit/auth-oauth-user": "^5.1.4", + "@octokit/request": "^9.2.3", + "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.0" }, "engines": { @@ -916,14 +848,14 @@ } }, "node_modules/@octokit/auth-oauth-device": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-7.1.1.tgz", - "integrity": "sha512-HWl8lYueHonuyjrKKIup/1tiy0xcmQCdq5ikvMO1YwkNNkxb6DXfrPjrMYItNLyCP/o2H87WuijuE+SlBTT8eg==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-7.1.5.tgz", + "integrity": "sha512-lR00+k7+N6xeECj0JuXeULQ2TSBB/zjTAmNF2+vyGPDEFx1dgk1hTDmL13MjbSmzusuAmuJD8Pu39rjp9jH6yw==", "license": "MIT", "dependencies": { - "@octokit/oauth-methods": "^5.0.0", - "@octokit/request": "^9.0.0", - "@octokit/types": "^13.0.0", + "@octokit/oauth-methods": "^5.1.5", + "@octokit/request": "^9.2.3", + "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.0" }, "engines": { @@ -931,15 +863,15 @@ } }, "node_modules/@octokit/auth-oauth-user": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-5.1.1.tgz", - "integrity": "sha512-rRkMz0ErOppdvEfnemHJXgZ9vTPhBuC6yASeFaB7I2yLMd7QpjfrL1mnvRPlyKo+M6eeLxrKanXJ9Qte29SRsw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-5.1.6.tgz", + "integrity": "sha512-/R8vgeoulp7rJs+wfJ2LtXEVC7pjQTIqDab7wPKwVG6+2v/lUnCOub6vaHmysQBbb45FknM3tbHW8TOVqYHxCw==", "license": "MIT", "dependencies": { - "@octokit/auth-oauth-device": "^7.0.1", - "@octokit/oauth-methods": "^5.0.0", - "@octokit/request": "^9.0.1", - "@octokit/types": "^13.0.0", + "@octokit/auth-oauth-device": "^7.1.5", + "@octokit/oauth-methods": "^5.1.5", + "@octokit/request": "^9.2.3", + "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.0" }, "engines": { @@ -947,38 +879,38 @@ } }, "node_modules/@octokit/auth-token": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", - "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", + "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", "license": "MIT", "engines": { "node": ">= 18" } }, "node_modules/@octokit/auth-unauthenticated": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-6.1.0.tgz", - "integrity": "sha512-zPSmfrUAcspZH/lOFQnVnvjQZsIvmfApQH6GzJrkIunDooU1Su2qt2FfMTSVPRp7WLTQyC20Kd55lF+mIYaohQ==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-6.1.3.tgz", + "integrity": "sha512-d5gWJla3WdSl1yjbfMpET+hUSFCE15qM0KVSB0H1shyuJihf/RL1KqWoZMIaonHvlNojkL9XtLFp8QeLe+1iwA==", "license": "MIT", "dependencies": { - "@octokit/request-error": "^6.0.1", - "@octokit/types": "^13.0.0" + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/core": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", - "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz", + "integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==", "license": "MIT", "dependencies": { "@octokit/auth-token": "^5.0.0", - "@octokit/graphql": "^8.0.0", - "@octokit/request": "^9.0.0", - "@octokit/request-error": "^6.0.1", - "@octokit/types": "^13.0.0", + "@octokit/graphql": "^8.2.2", + "@octokit/request": "^9.2.3", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", "before-after-hook": "^3.0.2", "universal-user-agent": "^7.0.0" }, @@ -987,12 +919,12 @@ } }, "node_modules/@octokit/endpoint": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz", - "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", + "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.0.0", + "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.2" }, "engines": { @@ -1000,13 +932,13 @@ } }, "node_modules/@octokit/graphql": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", - "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz", + "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", "license": "MIT", "dependencies": { - "@octokit/request": "^9.0.0", - "@octokit/types": "^13.0.0", + "@octokit/request": "^9.2.3", + "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.0" }, "engines": { @@ -1014,17 +946,17 @@ } }, "node_modules/@octokit/oauth-app": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-7.1.3.tgz", - "integrity": "sha512-EHXbOpBkSGVVGF1W+NLMmsnSsJRkcrnVmDKt0TQYRBb6xWfWzoi9sBD4DIqZ8jGhOWO/V8t4fqFyJ4vDQDn9bg==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-7.1.6.tgz", + "integrity": "sha512-OMcMzY2WFARg80oJNFwWbY51TBUfLH4JGTy119cqiDawSFXSIBujxmpXiKbGWQlvfn0CxE6f7/+c6+Kr5hI2YA==", "license": "MIT", "dependencies": { - "@octokit/auth-oauth-app": "^8.0.0", - "@octokit/auth-oauth-user": "^5.0.1", - "@octokit/auth-unauthenticated": "^6.0.0-beta.1", - "@octokit/core": "^6.0.0", - "@octokit/oauth-authorization-url": "^7.0.0", - "@octokit/oauth-methods": "^5.0.0", + "@octokit/auth-oauth-app": "^8.1.3", + "@octokit/auth-oauth-user": "^5.1.3", + "@octokit/auth-unauthenticated": "^6.1.2", + "@octokit/core": "^6.1.4", + "@octokit/oauth-authorization-url": "^7.1.1", + "@octokit/oauth-methods": "^5.1.4", "@types/aws-lambda": "^8.10.83", "universal-user-agent": "^7.0.0" }, @@ -1042,30 +974,30 @@ } }, "node_modules/@octokit/oauth-methods": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-5.1.2.tgz", - "integrity": "sha512-C5lglRD+sBlbrhCUTxgJAFjWgJlmTx5bQ7Ch0+2uqRjYv7Cfb5xpX4WuSC9UgQna3sqRGBL9EImX9PvTpMaQ7g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-5.1.5.tgz", + "integrity": "sha512-Ev7K8bkYrYLhoOSZGVAGsLEscZQyq7XQONCBBAl2JdMg7IT3PQn/y8P0KjloPoYpI5UylqYrLeUcScaYWXwDvw==", "license": "MIT", "dependencies": { "@octokit/oauth-authorization-url": "^7.0.0", - "@octokit/request": "^9.1.0", - "@octokit/request-error": "^6.1.0", - "@octokit/types": "^13.0.0" + "@octokit/request": "^9.2.3", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/openapi-types": { - "version": "22.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", - "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", "license": "MIT" }, "node_modules/@octokit/openapi-webhooks-types": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-8.5.1.tgz", - "integrity": "sha512-i3h1b5zpGSB39ffBbYdSGuAd0NhBAwPyA3QV3LYi/lx4lsbZiu7u2UHgXVUR6EpvOI8REOuVh1DZTRfHoJDvuQ==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-11.0.0.tgz", + "integrity": "sha512-ZBzCFj98v3SuRM7oBas6BHZMJRadlnDoeFfvm1olVxZnYeU6Vh97FhPxyS5aLh5pN51GYv2I51l/hVUAVkGBlA==", "license": "MIT" }, "node_modules/@octokit/plugin-paginate-graphql": { @@ -1081,12 +1013,12 @@ } }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.6.tgz", - "integrity": "sha512-zcvqqf/+TicbTCa/Z+3w4eBJcAxCFymtc0UAIsR3dEVoNilWld4oXdscQ3laXamTszUZdusw97K8+DrbFiOwjw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-12.0.0.tgz", + "integrity": "sha512-MPd6WK1VtZ52lFrgZ0R2FlaoiWllzgqFHaSZxvp72NmoDeZ0m8GeJdg4oB6ctqMTYyrnDYp592Xma21mrgiyDA==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.6.2" + "@octokit/types": "^14.0.0" }, "engines": { "node": ">= 18" @@ -1096,12 +1028,12 @@ } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "13.2.6", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.6.tgz", - "integrity": "sha512-wMsdyHMjSfKjGINkdGKki06VEkgdEldIGstIEyGX0wbYHGByOwN/KiM+hAAlUwAtPkP3gvXtVQA9L3ITdV2tVw==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-14.0.0.tgz", + "integrity": "sha512-iQt6ovem4b7zZYZQtdv+PwgbL5VPq37th1m2x2TdkgimIDJpsi2A6Q/OI/23i/hR6z5mL0EgisNR4dcbmckSZQ==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.6.1" + "@octokit/types": "^14.0.0" }, "engines": { "node": ">= 18" @@ -1111,13 +1043,13 @@ } }, "node_modules/@octokit/plugin-retry": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.1.2.tgz", - "integrity": "sha512-XOWnPpH2kJ5VTwozsxGurw+svB2e61aWlmk5EVIYZPwFK5F9h4cyPyj9CIKRyMXMHSwpIsI3mPOdpMmrRhe7UQ==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.2.1.tgz", + "integrity": "sha512-wUc3gv0D6vNHpGxSaR3FlqJpTXGWgqmk607N9L3LvPL4QjaxDgX/1nY2mGpT37Khn+nlIXdljczkRnNdTTV3/A==", "license": "MIT", "dependencies": { - "@octokit/request-error": "^6.0.0", - "@octokit/types": "^13.0.0", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", "bottleneck": "^2.15.3" }, "engines": { @@ -1128,30 +1060,31 @@ } }, "node_modules/@octokit/plugin-throttling": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-9.3.2.tgz", - "integrity": "sha512-FqpvcTpIWFpMMwIeSoypoJXysSAQ3R+ALJhXXSG1HTP3YZOIeLmcNcimKaXxTcws+Sh6yoRl13SJ5r8sXc1Fhw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-10.0.0.tgz", + "integrity": "sha512-Kuq5/qs0DVYTHZuBAzCZStCzo2nKvVRo/TDNhCcpC2TKiOGz/DisXMCvjt3/b5kr6SCI1Y8eeeJTHBxxpFvZEg==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.0.0", + "@octokit/types": "^14.0.0", "bottleneck": "^2.15.3" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "@octokit/core": "^6.0.0" + "@octokit/core": "^6.1.3" } }, "node_modules/@octokit/request": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", - "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz", + "integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==", "license": "MIT", "dependencies": { - "@octokit/endpoint": "^10.0.0", - "@octokit/request-error": "^6.0.1", - "@octokit/types": "^13.1.0", + "@octokit/endpoint": "^10.1.4", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^2.0.0", "universal-user-agent": "^7.0.2" }, "engines": { @@ -1159,44 +1092,44 @@ } }, "node_modules/@octokit/request-error": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.5.tgz", - "integrity": "sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==", + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", + "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.0.0" + "@octokit/types": "^14.0.0" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/types": { - "version": "13.6.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.2.tgz", - "integrity": "sha512-WpbZfZUcZU77DrSW4wbsSgTPfKcp286q3ItaIgvSbBpZJlu6mnYXAkjZz6LVZPXkEvLIM8McanyZejKTYUHipA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^22.2.0" + "@octokit/openapi-types": "^25.1.0" } }, "node_modules/@octokit/webhooks": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-13.4.1.tgz", - "integrity": "sha512-I5YPUtfWidh+OzyrlDahJsUpkpGK0kCTmDRbuqGmlCUzOtxdEkX3R4d6Cd08ijQYwkVXQJanPdbKuZBeV2NMaA==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-13.9.0.tgz", + "integrity": "sha512-5Kva+/Gi7c+39d0/0MM/v/5RCZuwqm75fUD+t7Es3Iz/adui54GnjfNmJpkkPkXGC+5IWnEvgqwY6gstK/JlUQ==", "license": "MIT", "dependencies": { - "@octokit/openapi-webhooks-types": "8.5.1", - "@octokit/request-error": "^6.0.1", - "@octokit/webhooks-methods": "^5.0.0" + "@octokit/openapi-webhooks-types": "11.0.0", + "@octokit/request-error": "^6.1.7", + "@octokit/webhooks-methods": "^5.1.1" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/webhooks-methods": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-5.1.0.tgz", - "integrity": "sha512-yFZa3UH11VIxYnnoOYCVoJ3q4ChuSOk2IVBBQ0O3xtKX4x9bmKb/1t+Mxixv2iUhzMdOl1qeWJqEhouXXzB3rQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-5.1.1.tgz", + "integrity": "sha512-NGlEHZDseJTCj8TMMFehzwa9g7On4KJMPVHDSrHxCQumL6uSQR8wIkP/qesv52fXqV1BPf4pTxwtS31ldAt9Xg==", "license": "MIT", "engines": { "node": ">= 18" @@ -1341,7 +1274,7 @@ "node_modules/@project-gauntlet/tools": { "version": "0.9.0", "resolved": "git+ssh://git@github.com/project-gauntlet/tools.git#6b77be418d6eb48c4139979ff1b8d0350c5b5268", - "integrity": "sha512-2vPxXVErfhJKMEfjf9ZtxgXO5HV9VJ6sjSK73eQRxS55pO+OKGZ5I7b8elhkBhPcHXPlNgmx8ZAhuLXJ86NPYg==", + "integrity": "sha512-1ZJItC7YYgFlCXYMHe7loiIeBf3MqMzdID+wmzgl2FWQEYk7VI4wG8FQAPYNjKe3C3mTetBPCdMUhIYC4L5UnQ==", "dev": true, "dependencies": { "@grpc/grpc-js": "^1.12.5", @@ -1349,9 +1282,9 @@ "@rollup/plugin-commonjs": "^28.0.2", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.0", - "chalk": "^5.4.0", + "chalk": "^5.4.1", "commander": "^12.1.0", - "rollup": "^4.28.1", + "rollup": "^4.19.2", "rollup-plugin-cleandir": "^3.0.0", "rollup-plugin-typescript2": "^0.36.0", "simple-git": "^3.27.0", @@ -1483,9 +1416,9 @@ } }, "node_modules/@rollup/plugin-commonjs": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.2.tgz", - "integrity": "sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==", + "version": "28.0.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz", + "integrity": "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==", "dev": true, "license": "MIT", "dependencies": { @@ -1531,9 +1464,9 @@ } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.0.tgz", - "integrity": "sha512-0FPvAeVUT/zdWoO0jnb/V5BlBsUSNfkIOtFHzMO4H9MOklrmQFY6FduVHKucNb/aTFxvnGhj4MNj/T1oNdDfNg==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", + "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==", "dev": true, "license": "MIT", "dependencies": { @@ -1578,9 +1511,9 @@ } }, "node_modules/@rollup/plugin-typescript": { - "version": "12.1.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.1.2.tgz", - "integrity": "sha512-cdtSp154H5sv637uMr1a8OTWB0L1SWDSm1rDGiyfcGcvQ6cuTs4MDk2BVEBGysUWago4OJN4EQZqOTl/QY3Jgg==", + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.1.3.tgz", + "integrity": "sha512-gAx0AYwkyjqOw4JrZV34N/abvAobLhczyLkZ7FVL2UXPrO4zv8oqTfYT3DLBRan1EXasp4SUuEJXqPTk0gnJzw==", "dev": true, "license": "MIT", "dependencies": { @@ -1605,9 +1538,9 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", - "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", "dev": true, "license": "MIT", "dependencies": { @@ -1628,9 +1561,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", - "integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz", + "integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==", "cpu": [ "arm" ], @@ -1643,9 +1576,9 @@ "peer": true }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz", - "integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz", + "integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==", "cpu": [ "arm64" ], @@ -1658,9 +1591,9 @@ "peer": true }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz", - "integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz", + "integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==", "cpu": [ "arm64" ], @@ -1673,9 +1606,9 @@ "peer": true }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz", - "integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz", + "integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==", "cpu": [ "x64" ], @@ -1688,9 +1621,9 @@ "peer": true }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz", - "integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz", + "integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==", "cpu": [ "arm64" ], @@ -1703,9 +1636,9 @@ "peer": true }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz", - "integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz", + "integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==", "cpu": [ "x64" ], @@ -1718,9 +1651,9 @@ "peer": true }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz", - "integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz", + "integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==", "cpu": [ "arm" ], @@ -1733,9 +1666,9 @@ "peer": true }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz", - "integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz", + "integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==", "cpu": [ "arm" ], @@ -1748,9 +1681,9 @@ "peer": true }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz", - "integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz", + "integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==", "cpu": [ "arm64" ], @@ -1763,9 +1696,9 @@ "peer": true }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz", - "integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz", + "integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==", "cpu": [ "arm64" ], @@ -1778,9 +1711,9 @@ "peer": true }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz", - "integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz", + "integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==", "cpu": [ "loong64" ], @@ -1793,9 +1726,9 @@ "peer": true }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz", - "integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz", + "integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==", "cpu": [ "ppc64" ], @@ -1808,9 +1741,24 @@ "peer": true }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz", - "integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz", + "integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz", + "integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==", "cpu": [ "riscv64" ], @@ -1823,9 +1771,9 @@ "peer": true }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz", - "integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz", + "integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==", "cpu": [ "s390x" ], @@ -1838,9 +1786,9 @@ "peer": true }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz", - "integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz", + "integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==", "cpu": [ "x64" ], @@ -1853,9 +1801,9 @@ "peer": true }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz", - "integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz", + "integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==", "cpu": [ "x64" ], @@ -1868,9 +1816,9 @@ "peer": true }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz", - "integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz", + "integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==", "cpu": [ "arm64" ], @@ -1883,9 +1831,9 @@ "peer": true }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz", - "integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz", + "integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==", "cpu": [ "ia32" ], @@ -1898,9 +1846,9 @@ "peer": true }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz", - "integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz", + "integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==", "cpu": [ "x64" ], @@ -1914,23 +1862,23 @@ }, "node_modules/@std/async": { "name": "@jsr/std__async", - "version": "1.0.9", - "resolved": "https://npm.jsr.io/~/11/@jsr/std__async/1.0.9.tgz", - "integrity": "sha512-gJgMcOx2DqpWeiFKnLIli4FcjjXlkJlMyPtHF+r7bGXI3FQPXEIpy8Z8A7/o3hjMyh8vSDdPijPpiLfPU91iUg==" + "version": "1.0.13", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__async/1.0.13.tgz", + "integrity": "sha512-GEApyNtzauJ0kEZ/GxebSkdEN0t29qJtkw+WEvzYTwkL6fHX8cq3YWzRjCqHu+4jMl+rpHiwyr/lfitNInntzA==" }, "node_modules/@std/fs": { "name": "@jsr/std__fs", - "version": "1.0.8", - "resolved": "https://npm.jsr.io/~/11/@jsr/std__fs/1.0.8.tgz", - "integrity": "sha512-w2BNxizm0BLiNNYRM+flC1Y8r6aqaol1supvGvDtAPMI7x5CBBoHejpsVfWT9if+VGLNAMMq5g4syzWumd4JQg==", + "version": "1.0.18", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__fs/1.0.18.tgz", + "integrity": "sha512-5l8uHzraoOXWVk+cWU3DFPXoZ9XQvuU8C143Ad65bgutjaHFcbPqJDPIPliHpJcby6jCFT1uT4mh/Ho/QrrEPQ==", "dependencies": { - "@jsr/std__path": "^1.0.8" + "@jsr/std__path": "^1.1.0" } }, "node_modules/@types/aws-lambda": { - "version": "8.10.146", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.146.tgz", - "integrity": "sha512-3BaDXYTh0e6UCJYL/jwV/3+GRslSc08toAiZSmleYtkAUyV5rtvdPYxrG/88uqvTuT6sb27WE9OS90ZNTIuQ0g==", + "version": "8.10.150", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.150.tgz", + "integrity": "sha512-AX+AbjH/rH5ezX1fbK8onC/a+HyQHo7QGmvoxAE42n22OsciAxvZoZNEr22tbXs8WfP1nIsBjKDpgPm3HjOZbA==", "license": "MIT" }, "node_modules/@types/cross-spawn": { @@ -1944,46 +1892,46 @@ } }, "node_modules/@types/deno": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/deno/-/deno-2.0.0.tgz", - "integrity": "sha512-O9/jRVlq93kqfkl4sYR5N7+Pz4ukzXVIbMnE/VgvpauNHsvjQ9iBVnJ3X0gAvMa2khcoFD8DSO7mQVCuiuDMPg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@types/deno/-/deno-2.3.0.tgz", + "integrity": "sha512-/4SyefQpKjwNKGkq9qG3Ln7MazfbWKvydyVFBnXzP5OQA4u1paoFtaOe1iHKycIWHHkhYag0lPxyheThV1ijzw==", "dev": true, "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, "node_modules/@types/lodash": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", - "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", + "version": "4.17.18", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.18.tgz", + "integrity": "sha512-KJ65INaxqxmU6EoCiJmRPZC9H9RVWCRd349tXM2M3O5NA7cY6YL7c0bHAHQ93NOfTObEQ004kd2QVHs/r0+m4g==", "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "version": "22.15.32", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.32.tgz", + "integrity": "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.20.0" + "undici-types": "~6.21.0" } }, "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", "dev": true, "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", - "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "version": "18.3.23", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", + "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", "dev": true, "license": "MIT", "dependencies": { @@ -2034,13 +1982,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/before-after-hook": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", @@ -2053,20 +1994,10 @@ "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", "license": "MIT" }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/chalk": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.0.tgz", - "integrity": "sha512-ZkD35Mx92acjB2yNJgziGqT9oKHEOxjTBTDRpOsRWtdecL/0jM3z5kM/CTzHWvHIen1GvkM85p6TuFfDGfc8/Q==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "license": "MIT", "engines": { @@ -2228,9 +2159,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2285,10 +2216,26 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/fdir": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", - "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2333,13 +2280,13 @@ } }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -2401,15 +2348,15 @@ } }, "node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", "dev": true, "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -2445,9 +2392,9 @@ } }, "node_modules/is-core-module": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", - "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { @@ -2494,9 +2441,9 @@ "license": "ISC" }, "node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -2555,9 +2502,9 @@ "license": "MIT" }, "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "dev": true, "license": "Apache-2.0" }, @@ -2574,9 +2521,9 @@ } }, "node_modules/lru-cache": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", - "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", "dev": true, "license": "ISC", "engines": { @@ -2620,13 +2567,13 @@ } }, "node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { "node": "20 || >=22" @@ -2652,21 +2599,22 @@ "license": "MIT" }, "node_modules/octokit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/octokit/-/octokit-4.0.2.tgz", - "integrity": "sha512-wbqF4uc1YbcldtiBFfkSnquHtECEIpYD78YUXI6ri1Im5OO2NLo6ZVpRdbJpdnpZ05zMrVPssNiEo6JQtea+Qg==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/octokit/-/octokit-4.1.4.tgz", + "integrity": "sha512-cRvxRte6FU3vAHRC9+PMSY3D+mRAs2Rd9emMoqp70UGRvJRM3sbAoim2IXRZNNsf8wVfn4sGxVBHRAP+JBVX/g==", "license": "MIT", "dependencies": { - "@octokit/app": "^15.0.0", - "@octokit/core": "^6.0.0", - "@octokit/oauth-app": "^7.0.0", - "@octokit/plugin-paginate-graphql": "^5.0.0", - "@octokit/plugin-paginate-rest": "^11.0.0", - "@octokit/plugin-rest-endpoint-methods": "^13.0.0", - "@octokit/plugin-retry": "^7.0.0", - "@octokit/plugin-throttling": "^9.0.0", - "@octokit/request-error": "^6.0.0", - "@octokit/types": "^13.0.0" + "@octokit/app": "^15.1.6", + "@octokit/core": "^6.1.5", + "@octokit/oauth-app": "^7.1.6", + "@octokit/plugin-paginate-graphql": "^5.2.4", + "@octokit/plugin-paginate-rest": "^12.0.0", + "@octokit/plugin-rest-endpoint-methods": "^14.0.0", + "@octokit/plugin-retry": "^7.2.1", + "@octokit/plugin-throttling": "^10.0.0", + "@octokit/request-error": "^6.1.8", + "@octokit/types": "^14.0.0", + "@octokit/webhooks": "^13.8.3" }, "engines": { "node": ">= 18" @@ -2788,9 +2736,9 @@ } }, "node_modules/protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz", + "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==", "dev": true, "hasInstallScript": true, "license": "BSD-3-Clause", @@ -2872,13 +2820,13 @@ } }, "node_modules/rollup": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.1.tgz", - "integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==", + "version": "4.44.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz", + "integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -2888,25 +2836,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.28.1", - "@rollup/rollup-android-arm64": "4.28.1", - "@rollup/rollup-darwin-arm64": "4.28.1", - "@rollup/rollup-darwin-x64": "4.28.1", - "@rollup/rollup-freebsd-arm64": "4.28.1", - "@rollup/rollup-freebsd-x64": "4.28.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.28.1", - "@rollup/rollup-linux-arm-musleabihf": "4.28.1", - "@rollup/rollup-linux-arm64-gnu": "4.28.1", - "@rollup/rollup-linux-arm64-musl": "4.28.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.28.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.28.1", - "@rollup/rollup-linux-riscv64-gnu": "4.28.1", - "@rollup/rollup-linux-s390x-gnu": "4.28.1", - "@rollup/rollup-linux-x64-gnu": "4.28.1", - "@rollup/rollup-linux-x64-musl": "4.28.1", - "@rollup/rollup-win32-arm64-msvc": "4.28.1", - "@rollup/rollup-win32-ia32-msvc": "4.28.1", - "@rollup/rollup-win32-x64-msvc": "4.28.1", + "@rollup/rollup-android-arm-eabi": "4.44.0", + "@rollup/rollup-android-arm64": "4.44.0", + "@rollup/rollup-darwin-arm64": "4.44.0", + "@rollup/rollup-darwin-x64": "4.44.0", + "@rollup/rollup-freebsd-arm64": "4.44.0", + "@rollup/rollup-freebsd-x64": "4.44.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", + "@rollup/rollup-linux-arm-musleabihf": "4.44.0", + "@rollup/rollup-linux-arm64-gnu": "4.44.0", + "@rollup/rollup-linux-arm64-musl": "4.44.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-gnu": "4.44.0", + "@rollup/rollup-linux-riscv64-musl": "4.44.0", + "@rollup/rollup-linux-s390x-gnu": "4.44.0", + "@rollup/rollup-linux-x64-gnu": "4.44.0", + "@rollup/rollup-linux-x64-musl": "4.44.0", + "@rollup/rollup-win32-arm64-msvc": "4.44.0", + "@rollup/rollup-win32-ia32-msvc": "4.44.0", + "@rollup/rollup-win32-x64-msvc": "4.44.0", "fsevents": "~2.3.2" } }, @@ -2978,9 +2927,9 @@ } }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -3025,14 +2974,14 @@ } }, "node_modules/simple-git": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.27.0.tgz", - "integrity": "sha512-ivHoFS9Yi9GY49ogc6/YAi3Fl9ROnF4VyubNylgCkA+RVqLaKWnDSzXOVzya8csELIaWaYNutsEuAhZrtOjozA==", + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz", + "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==", "license": "MIT", "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", - "debug": "^4.3.5" + "debug": "^4.4.0" }, "funding": { "type": "github", @@ -3199,9 +3148,9 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3213,9 +3162,9 @@ } }, "node_modules/undici": { - "version": "5.28.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", - "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", "license": "MIT", "dependencies": { "@fastify/busboy": "^2.0.0" @@ -3225,22 +3174,22 @@ } }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, "node_modules/universal-github-app-jwt": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-2.2.0.tgz", - "integrity": "sha512-G5o6f95b5BggDGuUfKDApKaCgNYy2x7OdHY0zSMF081O0EJobw+1130VONhrA7ezGSV2FNOGyM+KQpQZAr9bIQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz", + "integrity": "sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==", "license": "MIT" }, "node_modules/universal-user-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", - "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", "license": "ISC" }, "node_modules/universalify": { @@ -3451,9 +3400,9 @@ } }, "node_modules/zod": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", - "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "version": "3.25.67", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", + "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", "dev": true, "license": "MIT", "funding": { @@ -3461,110 +3410,16 @@ } }, "node_modules/zod-validation-error": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.4.0.tgz", - "integrity": "sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.2.tgz", + "integrity": "sha512-mdi7YOLtram5dzJ5aDtm1AG9+mxRma1iaMrZdYIpFO7epdKBUwLHIxTF8CPDeCQ828zAXYtizrKlEJAtzgfgrw==", "dev": true, "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "zod": "^3.18.0" - } - }, - "scenarios/js/api": { - "extraneous": true - }, - "scenarios/plugins/docs_detail": { - "name": "@project-gauntlet/docs-detailt", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "scenarios/plugins/docs_form": { - "name": "@project-gauntlet/docs-form", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "scenarios/plugins/docs_grid": { - "name": "@project-gauntlet/docs-grid", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "scenarios/plugins/docs_inline_separators": { - "name": "@project-gauntlet/docs-inline-separators", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "scenarios/plugins/docs_inline_three_sections": { - "name": "@project-gauntlet/docs-inline-three-sections", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "scenarios/plugins/docs_inline_two_sections": { - "name": "@project-gauntlet/docs-inline-two-sections", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" - } - }, - "scenarios/plugins/docs_list": { - "name": "@project-gauntlet/docs-list", - "extraneous": true, - "dependencies": { - "@project-gauntlet/api": "file:../../js/api" - }, - "devDependencies": { - "@project-gauntlet/tools": "*", - "@types/deno": "*", - "@types/react": "*", - "typescript": "*" + "zod": "^3.25.0" } } } From a0bbad9feaec9f09774f409e62d041df21dbc03a Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 19 Jun 2025 16:24:55 +0200 Subject: [PATCH 40/91] Slightly refactor window management behaviour into separate module --- rust/client/src/ui/mod.rs | 347 +++++------------- rust/client/src/ui/platform/mod.rs | 2 - rust/client/src/ui/state/mod.rs | 7 +- .../src/ui/{hud/mod.rs => windows/hud.rs} | 30 +- rust/client/src/ui/windows/mod.rs | 245 +++++++++++++ .../linux.rs => windows/x11_focus.rs} | 10 +- 6 files changed, 363 insertions(+), 278 deletions(-) delete mode 100644 rust/client/src/ui/platform/mod.rs rename rust/client/src/ui/{hud/mod.rs => windows/hud.rs} (80%) create mode 100644 rust/client/src/ui/windows/mod.rs rename rust/client/src/ui/{platform/linux.rs => windows/x11_focus.rs} (86%) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index ac87371..bd0495e 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -39,17 +39,14 @@ use gauntlet_utils::channel::Responder; use gauntlet_utils::channel::channel; use iced::Event; use iced::Length; -use iced::Point; use iced::Renderer; use iced::Settings; -use iced::Size; use iced::Subscription; use iced::Task; use iced::advanced::graphics::core::SmolStr; use iced::alignment::Horizontal; use iced::alignment::Vertical; use iced::event; -use iced::font; use iced::futures::SinkExt; use iced::futures::StreamExt; use iced::futures::channel::mpsc; @@ -70,9 +67,6 @@ use iced::widget::text::Shaping; use iced::widget::text_input; use iced::widget::text_input::focus; use iced::window; -use iced::window::Level; -use iced::window::Mode; -use iced::window::Position; use iced::window::Screenshot; use iced_fonts::BOOTSTRAP_FONT_BYTES; use tokio::sync::RwLock as TokioRwLock; @@ -88,7 +82,6 @@ use crate::ui::theme::text_input::TextInputStyle; mod client_context; mod custom_widgets; mod grid_navigation; -mod hud; mod scroll_handle; mod search_list; mod state; @@ -98,14 +91,13 @@ mod theme; mod widget; mod widget_container; -mod platform; +mod windows; pub use theme::GauntletComplexTheme; +#[cfg(target_os = "linux")] +use windows::x11_focus::listen_on_x11_active_window_change; use crate::ui::custom_widgets::loading_bar::LoadingBar; -use crate::ui::hud::show_hud_window; -#[cfg(target_os = "linux")] -use crate::ui::platform::linux::listen_on_x11_active_window_change; use crate::ui::scroll_handle::ScrollHandle; use crate::ui::state::ErrorViewData; use crate::ui::state::Focus; @@ -118,6 +110,11 @@ use crate::ui::widget::action_panel::ActionPanel; use crate::ui::widget::action_panel::ActionPanelItem; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::root::render_root; +use crate::ui::windows::WindowActionMsg; +use crate::ui::windows::create_window; +use crate::ui::windows::hide_window; +use crate::ui::windows::hud::show_hud_window; +use crate::ui::windows::show_window; pub struct AppModel { // logic @@ -149,13 +146,6 @@ pub struct AppModel { hud_display: Option, } -#[cfg(target_os = "linux")] -mod layer_shell { - #[iced_layershell::to_layer_message(multi)] - #[derive(Debug, Clone)] - pub enum LayerShellAppMsg {} -} - #[derive(Debug, Clone)] pub enum AppMsg { OpenView { @@ -218,9 +208,6 @@ pub enum AppMsg { widget_event: ComponentWidgetEvent, }, Noop, - ShowWindow, - HideWindow, - ToggleWindow, HandleGlobalShortcut(GlobalShortcutPressedEvent), ToggleActionPanel { keyboard: bool, @@ -255,9 +242,6 @@ pub enum AppMsg { InlineViewShortcuts { shortcuts: HashMap>, }, - ShowHud { - display: String, - }, OnPrimaryActionMainViewNoPanelKeyboardWithoutFocus, OnPrimaryActionMainViewNoPanel { search_result: SearchResult, @@ -303,22 +287,12 @@ pub enum AppMsg { FocusPluginViewSearchBar { widget_id: UiWidgetId, }, - #[cfg(target_os = "linux")] - LayerShell(layer_shell::LayerShellAppMsg), ClearInlineView { plugin_id: PluginId, }, SetTheme { theme: UiTheme, }, - SetWindowPositionMode { - mode: WindowPositionMode, - }, - #[cfg(target_os = "linux")] - X11ActiveWindowChanged { - window: u32, - wm_name: Option, - }, RunEntrypoint { plugin_id: PluginId, entrypoint_id: EntrypointId, @@ -327,119 +301,7 @@ pub enum AppMsg { request_data: Arc, responder: Arc>>>, }, -} - -#[cfg(target_os = "linux")] -impl TryInto for AppMsg { - type Error = Self; - fn try_into(self) -> Result { - match self { - Self::LayerShell(msg) => msg.try_into().map_err(|msg| Self::LayerShell(msg)), - _ => Err(self), - } - } -} - -const WINDOW_WIDTH: f32 = 750.0; -const WINDOW_HEIGHT: f32 = 450.0; - -#[cfg(not(target_os = "macos"))] -fn window_settings(visible: bool, position: Position) -> window::Settings { - window::Settings { - size: Size::new(WINDOW_WIDTH, WINDOW_HEIGHT), - position, - resizable: false, - decorations: false, - visible, - transparent: true, - closeable: false, - minimizable: false, - #[cfg(target_os = "linux")] - platform_specific: window::settings::PlatformSpecific { - application_id: "gauntlet".to_string(), - ..Default::default() - }, - ..Default::default() - } -} - -#[cfg(target_os = "macos")] -fn window_settings(visible: bool, position: Position) -> window::Settings { - window::Settings { - size: Size::new(WINDOW_WIDTH, WINDOW_HEIGHT), - position, - resizable: false, - decorations: true, - visible, - transparent: false, - closeable: false, - minimizable: false, - platform_specific: window::settings::PlatformSpecific { - window_kind: window::settings::WindowKind::Popup, - fullsize_content_view: true, - title_hidden: true, - titlebar_transparent: true, - ..Default::default() - }, - ..Default::default() - } -} - -#[cfg(target_os = "linux")] -fn layer_shell_settings() -> iced_layershell::reexport::NewLayerShellSettings { - iced_layershell::reexport::NewLayerShellSettings { - layer: iced_layershell::reexport::Layer::Overlay, - keyboard_interactivity: iced_layershell::reexport::KeyboardInteractivity::Exclusive, - events_transparent: false, - anchor: iced_layershell::reexport::Anchor::empty(), - margin: Default::default(), - exclusive_zone: Some(0), - size: Some((WINDOW_WIDTH as u32, WINDOW_HEIGHT as u32)), - use_last_output: false, - namespace: None, - } -} - -fn open_main_window_non_wayland(minimized: bool, window_position_file: Option<&PathBuf>) -> (window::Id, Task) { - let position = window_position_file - .map(|window_position_file| fs::read_to_string(window_position_file).ok()) - .flatten() - .map(|data| { - if let Some((x, y)) = data.split_once(":") { - match (x.parse(), y.parse()) { - (Ok(x), Ok(y)) => Some(Position::Specific(Point::new(x, y))), - _ => None, - } - } else { - None - } - }) - .unwrap_or(None) - .unwrap_or(Position::Centered); - - let (main_window_id, open_task) = window::open(window_settings(!minimized, position)); - - ( - main_window_id, - Task::batch([ - open_task.map(|_| AppMsg::Noop), - window::gain_focus(main_window_id), - window::set_level(main_window_id, Level::AlwaysOnTop), - ]), - ) -} - -#[cfg(target_os = "linux")] -fn open_main_window_wayland(id: window::Id) -> (window::Id, Task) { - let settings = layer_shell_settings(); - - ( - id, - Task::done(AppMsg::LayerShell(layer_shell::LayerShellAppMsg::NewLayerShell { - id, - settings, - })), - ) + WindowAction(WindowActionMsg), } pub fn run(minimized: bool) { @@ -529,25 +391,16 @@ fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, let theme = GauntletComplexTheme::new(setup_data.theme); GauntletComplexTheme::set_global(theme.clone()); - let mut tasks = vec![]; + let mut tasks: Vec> = vec![]; - #[cfg(target_os = "linux")] - let (main_window_id, open_task) = if wayland { - let id = window::Id::unique(); + let (main_window_id, open_task) = create_window( + #[cfg(target_os = "linux")] + wayland, + minimized, + setup_data.window_position_file.as_ref(), + ); - if minimized { - (id, Task::none()) - } else { - open_main_window_wayland(id) - } - } else { - open_main_window_non_wayland(minimized, setup_data.window_position_file.as_ref()) - }; - - #[cfg(not(target_os = "linux"))] - let (main_window_id, open_task) = open_main_window_non_wayland(minimized, setup_data.window_position_file.as_ref()); - - tasks.push(open_task); + tasks.push(open_task.map(AppMsg::WindowAction)); tasks.push(Task::stream(stream::channel(10, |mut sender| { async move { @@ -1145,9 +998,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { render_location, } => state.handle_plugin_event(widget_event, plugin_id, render_location), AppMsg::Noop => Task::none(), - AppMsg::ToggleWindow => state.toggle_window(), - AppMsg::ShowWindow => state.show_window(), - AppMsg::HideWindow => state.hide_window(true), AppMsg::ShowPreferenceRequiredView { plugin_id, entrypoint_id, @@ -1428,14 +1278,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } - AppMsg::ShowHud { display } => { - state.hud_display = Some(display); - - show_hud_window( - #[cfg(target_os = "linux")] - state.wayland, - ) - } AppMsg::ResetMainViewState => { match &mut state.global_state { GlobalState::MainView { sub_state, .. } => { @@ -1493,10 +1335,48 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } AppMsg::FocusPluginViewSearchBar { widget_id } => state.client_context.focus_search_bar(widget_id), - #[cfg(target_os = "linux")] - AppMsg::LayerShell(_) => { - // handled by library - Task::none() + AppMsg::WindowAction(action) => { + match action { + #[cfg(target_os = "linux")] + WindowActionMsg::LayerShell(_) => { + // handled by library + Task::none() + } + WindowActionMsg::SetWindowPositionMode { mode } => { + state.window_position_mode = mode; + + Task::none() + } + #[cfg(target_os = "linux")] + WindowActionMsg::X11ActiveWindowChanged { window, wm_name } => { + if state.x11_active_window != Some(window) { + state.x11_active_window = Some(window); + if let Some(wm_name) = &wm_name { + if wm_name != "gauntlet" { + Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)) + } else { + Task::none() + } + } else { + Task::none() + } + } else { + Task::none() + } + } + WindowActionMsg::ToggleWindow => state.toggle_window(), + WindowActionMsg::ShowWindow => state.show_window(), + WindowActionMsg::HideWindow => state.hide_window(true), + WindowActionMsg::ShowHud { display } => { + state.hud_display = Some(display); + + show_hud_window( + #[cfg(target_os = "linux")] + state.wayland, + ) + .map(AppMsg::WindowAction) + } + } } AppMsg::ClearInlineView { plugin_id } => { state.client_context.clear_inline_view(&plugin_id); @@ -1510,11 +1390,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } - AppMsg::SetWindowPositionMode { mode } => { - state.window_position_mode = mode; - - Task::none() - } AppMsg::ShowNewView { plugin_id, entrypoint_id, @@ -1530,7 +1405,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { }, ), Task::done(AppMsg::OpenPluginView(plugin_id, entrypoint_id)), - Task::done(AppMsg::ShowWindow), + Task::done(AppMsg::WindowAction(WindowActionMsg::ShowWindow)), ]) } AppMsg::ShowNewGeneratedView { @@ -1549,31 +1424,16 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { }, ), state.run_generated_entrypoint(plugin_id, entrypoint_id, action_index), - Task::done(AppMsg::ShowWindow), + Task::done(AppMsg::WindowAction(WindowActionMsg::ShowWindow)), ]) } - #[cfg(target_os = "linux")] - AppMsg::X11ActiveWindowChanged { window, wm_name } => { - if state.x11_active_window != Some(window) { - state.x11_active_window = Some(window); - if let Some(wm_name) = &wm_name { - if wm_name != "gauntlet" { - Task::done(AppMsg::HideWindow) - } else { - Task::none() - } - } else { - Task::none() - } - } else { - Task::none() - } - } AppMsg::HandleGlobalShortcut(event) => { match state.application_manager.handle_global_shortcut_event(event) { Ok(action) => { match action { - GlobalShortcutAction::ToggleWindow => Task::done(AppMsg::ToggleWindow), + GlobalShortcutAction::ToggleWindow => { + Task::done(AppMsg::WindowAction(WindowActionMsg::ToggleWindow)) + } GlobalShortcutAction::RunEntrypoint { plugin_id, entrypoint_id, @@ -1740,7 +1600,9 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { let button_label: Element<_> = text("Close").into(); - let button: Element<_> = button(button_label).on_press(AppMsg::HideWindow).into(); + let button: Element<_> = button(button_label) + .on_press(AppMsg::WindowAction(WindowActionMsg::HideWindow)) + .into(); let button = container(button).width(Length::Fill).align_x(Horizontal::Center).into(); @@ -1781,7 +1643,9 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { let button_label: Element<_> = text("Close").into(); - let button: Element<_> = button(button_label).on_press(AppMsg::HideWindow).into(); + let button: Element<_> = button(button_label) + .on_press(AppMsg::WindowAction(WindowActionMsg::HideWindow)) + .into(); let button = container(button).width(Length::Fill).align_x(Horizontal::Center).into(); @@ -1814,7 +1678,9 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { let button_label: Element<_> = text("Close").into(); - let button: Element<_> = button(button_label).on_press(AppMsg::HideWindow).into(); + let button: Element<_> = button(button_label) + .on_press(AppMsg::WindowAction(WindowActionMsg::HideWindow)) + .into(); let button = container(button).width(Length::Fill).align_x(Horizontal::Center).into(); @@ -2240,28 +2106,14 @@ impl AppModel { self.pending_window_state_reset = reset_state; - #[cfg(target_os = "linux")] - if self.wayland { - commands.push(Task::done(AppMsg::LayerShell( - layer_shell::LayerShellAppMsg::RemoveWindow(self.main_window_id), - ))); - } else { - commands.push(window::set_mode(self.main_window_id, Mode::Hidden)); - }; - - #[cfg(not(target_os = "linux"))] - commands.push(window::set_mode(self.main_window_id, Mode::Hidden)); - - #[cfg(target_os = "macos")] - unsafe { - // when closing NSPanel current active application doesn't automatically become key window - // is there a proper way? without doing this manually - let app = objc2_app_kit::NSWorkspace::sharedWorkspace().menuBarOwningApplication(); - - if let Some(app) = app { - app.activateWithOptions(objc2_app_kit::NSApplicationActivationOptions::empty()); - } - } + commands.push( + hide_window( + #[cfg(target_os = "linux")] + self.wayland, + self.main_window_id, + ) + .map(AppMsg::WindowAction), + ); match &self.global_state { GlobalState::PluginView { @@ -2295,29 +2147,14 @@ impl AppModel { self.opened = true; - #[cfg(target_os = "linux")] - let open_task = if self.wayland { - let (_, open_task) = open_main_window_wayland(self.main_window_id); - open_task - } else { - Task::batch([ - window::gain_focus(self.main_window_id), - window::set_mode(self.main_window_id, Mode::Windowed), - ]) - }; - - #[cfg(not(target_os = "linux"))] - let open_task = Task::batch([ - window::gain_focus(self.main_window_id), + show_window( + self.main_window_id, + #[cfg(target_os = "linux")] + self.wayland, #[cfg(target_os = "macos")] - match self.window_position_mode { - WindowPositionMode::Static => Task::none(), - WindowPositionMode::ActiveMonitor => window::move_to_active_monitor(self.main_window_id), - }, - window::set_mode(self.main_window_id, Mode::Windowed), - ]); - - open_task + self.window_position_mode, + ) + .map(AppMsg::WindowAction) } fn reset_window_state(&mut self) -> Task { @@ -2783,12 +2620,12 @@ async fn request_loop( FrontendApiRequestData::ShowWindow {} => { responder.respond(Ok(FrontendApiResponseData::ShowWindow { data: () })); - AppMsg::ShowWindow + AppMsg::WindowAction(WindowActionMsg::ShowWindow) } FrontendApiRequestData::HideWindow {} => { responder.respond(Ok(FrontendApiResponseData::HideWindow { data: () })); - AppMsg::HideWindow + AppMsg::WindowAction(WindowActionMsg::HideWindow) } FrontendApiRequestData::ShowPreferenceRequiredView { plugin_id, @@ -2825,7 +2662,7 @@ async fn request_loop( FrontendApiRequestData::ShowHud { display } => { responder.respond(Ok(FrontendApiResponseData::ShowHud { data: () })); - AppMsg::ShowHud { display } + AppMsg::WindowAction(WindowActionMsg::ShowHud { display }) } FrontendApiRequestData::UpdateLoadingBar { plugin_id, @@ -2848,7 +2685,7 @@ async fn request_loop( FrontendApiRequestData::SetWindowPositionMode { mode } => { responder.respond(Ok(FrontendApiResponseData::SetWindowPositionMode { data: () })); - AppMsg::SetWindowPositionMode { mode } + AppMsg::WindowAction(WindowActionMsg::SetWindowPositionMode { mode }) } FrontendApiRequestData::OpenPluginView { plugin_id, diff --git a/rust/client/src/ui/platform/mod.rs b/rust/client/src/ui/platform/mod.rs deleted file mode 100644 index 8daba25..0000000 --- a/rust/client/src/ui/platform/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(target_os = "linux")] -pub mod linux; diff --git a/rust/client/src/ui/state/mod.rs b/rust/client/src/ui/state/mod.rs index f311e6a..2d962ff 100644 --- a/rust/client/src/ui/state/mod.rs +++ b/rust/client/src/ui/state/mod.rs @@ -17,6 +17,7 @@ use crate::ui::scroll_handle::ESTIMATED_MAIN_LIST_ITEM_HEIGHT; use crate::ui::scroll_handle::ScrollHandle; pub use crate::ui::state::main_view::MainViewState; pub use crate::ui::state::plugin_view::PluginViewState; +use crate::ui::windows::WindowActionMsg; pub enum GlobalState { MainView { @@ -286,7 +287,7 @@ impl Focus for GlobalState { match self { GlobalState::MainView { sub_state, .. } => { match sub_state { - MainViewState::None => Task::done(AppMsg::HideWindow), + MainViewState::None => Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)), MainViewState::SearchResultActionPanel { .. } => { MainViewState::initial(sub_state); Task::none() @@ -316,7 +317,7 @@ impl Focus for GlobalState { if *close_window_on_esc { Task::batch([ Task::done(AppMsg::ClosePluginView(plugin_id)), - Task::done(AppMsg::HideWindow), + Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)), ]) } else { Task::batch([ @@ -333,7 +334,7 @@ impl Focus for GlobalState { PluginViewState::ActionPanel { .. } => Task::done(AppMsg::ToggleActionPanel { keyboard: true }), } } - GlobalState::ErrorView { .. } => Task::done(AppMsg::HideWindow), + GlobalState::ErrorView { .. } => Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)), GlobalState::PendingPluginView { .. } => Task::none(), } } diff --git a/rust/client/src/ui/hud/mod.rs b/rust/client/src/ui/windows/hud.rs similarity index 80% rename from rust/client/src/ui/hud/mod.rs rename to rust/client/src/ui/windows/hud.rs index 771c181..bba5e6c 100644 --- a/rust/client/src/ui/hud/mod.rs +++ b/rust/client/src/ui/windows/hud.rs @@ -1,4 +1,3 @@ -use std::convert; use std::time::Duration; use iced::Point; @@ -9,12 +8,13 @@ use iced::window::Level; use iced::window::Position; use iced::window::Settings; -use crate::ui::AppMsg; +use crate::ui::windows::WindowActionMsg; +use crate::ui::windows::layer_shell; const HUD_WINDOW_WIDTH: f32 = 400.0; const HUD_WINDOW_HEIGHT: f32 = 40.0; -pub fn show_hud_window(#[cfg(target_os = "linux")] wayland: bool) -> Task { +pub fn show_hud_window(#[cfg(target_os = "linux")] wayland: bool) -> Task { #[cfg(target_os = "linux")] if wayland { open_wayland() @@ -26,7 +26,7 @@ pub fn show_hud_window(#[cfg(target_os = "linux")] wayland: bool) -> Task Task { +fn open_non_wayland() -> Task { let settings = Settings { size: Size::new(HUD_WINDOW_WIDTH, HUD_WINDOW_HEIGHT), position: Position::SpecificWith(|window, screen| { @@ -56,17 +56,17 @@ fn open_non_wayland() -> Task { } #[cfg(target_os = "linux")] -fn open_wayland() -> Task { +fn open_wayland() -> Task { let id = window::Id::unique(); let settings = layer_shell_settings(); Task::batch([ - Task::done(AppMsg::LayerShell( - crate::ui::layer_shell::LayerShellAppMsg::NewLayerShell { id, settings }, + Task::done(WindowActionMsg::LayerShell( + layer_shell::LayerShellAppMsg::NewLayerShell { id, settings }, )), sleep_for_2_seconds(id).then(|id| { - Task::done(AppMsg::LayerShell( - crate::ui::layer_shell::LayerShellAppMsg::RemoveWindow(id), + Task::done(WindowActionMsg::LayerShell( + layer_shell::LayerShellAppMsg::RemoveWindow(id), )) }), ]) @@ -88,12 +88,8 @@ fn layer_shell_settings() -> iced_layershell::reexport::NewLayerShellSettings { } fn sleep_for_2_seconds(id: window::Id) -> Task { - Task::perform( - async move { - tokio::time::sleep(Duration::from_secs(2)).await; - - id - }, - convert::identity, - ) + Task::future(async { + tokio::time::sleep(Duration::from_secs(2)).await; + }) + .then(move |_| Task::done(id)) } diff --git a/rust/client/src/ui/windows/mod.rs b/rust/client/src/ui/windows/mod.rs new file mode 100644 index 0000000..634de42 --- /dev/null +++ b/rust/client/src/ui/windows/mod.rs @@ -0,0 +1,245 @@ +use std::fs; +use std::path::PathBuf; + +use gauntlet_common::model::WindowPositionMode; +use iced::Point; +use iced::Size; +use iced::Task; +use iced::window; +use iced::window::Level; +use iced::window::Mode; +use iced::window::Position; + +use crate::ui::AppMsg; + +pub mod hud; +#[cfg(target_os = "linux")] +pub mod x11_focus; + +#[derive(Debug, Clone)] +pub enum WindowActionMsg { + #[cfg(target_os = "linux")] + LayerShell(layer_shell::LayerShellAppMsg), + SetWindowPositionMode { + mode: WindowPositionMode, + }, + #[cfg(target_os = "linux")] + X11ActiveWindowChanged { + window: u32, + wm_name: Option, + }, + ShowWindow, + HideWindow, + ToggleWindow, + ShowHud { + display: String, + }, +} + +#[cfg(target_os = "linux")] +mod layer_shell { + #[iced_layershell::to_layer_message(multi)] + #[derive(Debug, Clone)] + pub enum LayerShellAppMsg {} +} + +#[cfg(target_os = "linux")] +impl TryInto for AppMsg { + type Error = Self; + fn try_into(self) -> Result { + match self { + Self::WindowAction(WindowActionMsg::LayerShell(msg)) => { + msg.try_into() + .map_err(|msg| Self::WindowAction(WindowActionMsg::LayerShell(msg))) + } + _ => Err(self), + } + } +} + +const WINDOW_WIDTH: f32 = 750.0; +const WINDOW_HEIGHT: f32 = 450.0; + +#[cfg(not(target_os = "macos"))] +fn window_settings(visible: bool, position: Position) -> window::Settings { + window::Settings { + size: Size::new(WINDOW_WIDTH, WINDOW_HEIGHT), + position, + resizable: false, + decorations: false, + visible, + transparent: true, + closeable: false, + minimizable: false, + #[cfg(target_os = "linux")] + platform_specific: window::settings::PlatformSpecific { + application_id: "gauntlet".to_string(), + ..Default::default() + }, + ..Default::default() + } +} + +#[cfg(target_os = "macos")] +fn window_settings(visible: bool, position: Position) -> window::Settings { + window::Settings { + size: Size::new(WINDOW_WIDTH, WINDOW_HEIGHT), + position, + resizable: false, + decorations: true, + visible, + transparent: false, + closeable: false, + minimizable: false, + platform_specific: window::settings::PlatformSpecific { + window_kind: window::settings::WindowKind::Popup, + fullsize_content_view: true, + title_hidden: true, + titlebar_transparent: true, + ..Default::default() + }, + ..Default::default() + } +} + +pub fn create_window( + #[cfg(target_os = "linux")] wayland: bool, + minimized: bool, + window_position_file: Option<&PathBuf>, +) -> (window::Id, Task) { + #[cfg(target_os = "linux")] + let (main_window_id, open_task) = if wayland { + let id = window::Id::unique(); + + if minimized { + (id, Task::none()) + } else { + open_main_window_wayland(id) + } + } else { + open_main_window_non_wayland(minimized, window_position_file) + }; + + #[cfg(not(target_os = "linux"))] + let (main_window_id, open_task) = open_main_window_non_wayland(minimized, window_position_file); + + (main_window_id, open_task) +} + +pub fn show_window( + main_window_id: window::Id, + #[cfg(target_os = "linux")] wayland: bool, + #[cfg(target_os = "macos")] window_position_mode: WindowPositionMode, +) -> Task { + #[cfg(target_os = "linux")] + let open_task = if wayland { + let (_, open_task) = open_main_window_wayland(main_window_id); + open_task + } else { + Task::batch([ + window::gain_focus(main_window_id), + window::set_mode(main_window_id, Mode::Windowed), + ]) + }; + + #[cfg(not(target_os = "linux"))] + let open_task = Task::batch([ + window::gain_focus(main_window_id), + #[cfg(target_os = "macos")] + match window_position_mode { + WindowPositionMode::Static => Task::none(), + WindowPositionMode::ActiveMonitor => window::move_to_active_monitor(main_window_id), + }, + window::set_mode(main_window_id, Mode::Windowed), + ]); + + open_task +} + +pub fn hide_window(#[cfg(target_os = "linux")] wayland: bool, main_window_id: window::Id) -> Task { + let mut commands = vec![]; + + #[cfg(target_os = "linux")] + if wayland { + commands.push(Task::done(WindowActionMsg::LayerShell( + layer_shell::LayerShellAppMsg::RemoveWindow(main_window_id), + ))); + } else { + commands.push(window::set_mode(main_window_id, Mode::Hidden)); + }; + + #[cfg(not(target_os = "linux"))] + commands.push(window::set_mode(main_window_id, Mode::Hidden)); + + #[cfg(target_os = "macos")] + unsafe { + // when closing NSPanel current active application doesn't automatically become key window + // is there a proper way? without doing this manually + let app = objc2_app_kit::NSWorkspace::sharedWorkspace().menuBarOwningApplication(); + + if let Some(app) = app { + app.activateWithOptions(objc2_app_kit::NSApplicationActivationOptions::empty()); + } + } + + Task::batch(commands) +} + +#[cfg(target_os = "linux")] +fn layer_shell_settings() -> iced_layershell::reexport::NewLayerShellSettings { + iced_layershell::reexport::NewLayerShellSettings { + layer: iced_layershell::reexport::Layer::Overlay, + keyboard_interactivity: iced_layershell::reexport::KeyboardInteractivity::Exclusive, + events_transparent: false, + anchor: iced_layershell::reexport::Anchor::empty(), + margin: Default::default(), + exclusive_zone: Some(0), + size: Some((WINDOW_WIDTH as u32, WINDOW_HEIGHT as u32)), + use_last_output: false, + namespace: None, + } +} + +fn open_main_window_non_wayland( + minimized: bool, + window_position_file: Option<&PathBuf>, +) -> (window::Id, Task) { + let position = window_position_file + .map(|window_position_file| fs::read_to_string(window_position_file).ok()) + .flatten() + .map(|data| { + if let Some((x, y)) = data.split_once(":") { + match (x.parse(), y.parse()) { + (Ok(x), Ok(y)) => Some(Position::Specific(Point::new(x, y))), + _ => None, + } + } else { + None + } + }) + .unwrap_or(None) + .unwrap_or(Position::Centered); + + let (main_window_id, open_task) = window::open(window_settings(!minimized, position)); + + ( + main_window_id, + Task::batch([ + open_task.discard(), + window::gain_focus(main_window_id), + window::set_level(main_window_id, Level::AlwaysOnTop), + ]), + ) +} + +#[cfg(target_os = "linux")] +fn open_main_window_wayland(id: window::Id) -> (window::Id, Task) { + let settings = layer_shell_settings(); + + ( + id, + Task::done(WindowActionMsg::LayerShell( + layer_shell::LayerShellAppMsg::NewLayerShell { id, settings }, + )), + ) +} diff --git a/rust/client/src/ui/platform/linux.rs b/rust/client/src/ui/windows/x11_focus.rs similarity index 86% rename from rust/client/src/ui/platform/linux.rs rename to rust/client/src/ui/windows/x11_focus.rs index 49fcb16..8fff8af 100644 --- a/rust/client/src/ui/platform/linux.rs +++ b/rust/client/src/ui/windows/x11_focus.rs @@ -14,6 +14,7 @@ use x11rb::protocol::xproto::Window; use x11rb::rust_connection::RustConnection; use crate::ui::AppMsg; +use crate::ui::windows::WindowActionMsg; pub fn listen_on_x11_active_window_change(sender: Sender, handle: Handle) -> anyhow::Result { let (conn, screen_num) = RustConnection::connect(None)?; @@ -34,7 +35,14 @@ pub fn listen_on_x11_active_window_change(sender: Sender, handle: Handle let wm_name = fetch_app_wm_name(&conn, window).ok(); let mut sender = sender.clone(); - handle.spawn(async move { sender.send(AppMsg::X11ActiveWindowChanged { window, wm_name }).await }); + handle.spawn(async move { + sender + .send(AppMsg::WindowAction(WindowActionMsg::X11ActiveWindowChanged { + window, + wm_name, + })) + .await + }); } } } From 6645ce21ad7084dd3ea38efc891d61aabcb1b357 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 19 Jun 2025 18:34:35 +0200 Subject: [PATCH 41/91] Refactor server and window state of ui into separate modules --- rust/client/src/ui/mod.rs | 737 +++--------------------- rust/client/src/ui/server.rs | 477 +++++++++++++++ rust/client/src/ui/state/mod.rs | 9 +- rust/client/src/ui/windows/hud.rs | 6 +- rust/client/src/ui/windows/mod.rs | 200 ++++++- rust/client/src/ui/windows/x11_focus.rs | 18 +- 6 files changed, 765 insertions(+), 682 deletions(-) create mode 100644 rust/client/src/ui/server.rs diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index bd0495e..71661e9 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -1,8 +1,6 @@ use std::collections::HashMap; use std::fs; -use std::ops::Deref; use std::path::Path; -use std::path::PathBuf; use std::sync::Arc; use std::sync::Mutex; @@ -19,10 +17,6 @@ use gauntlet_common::model::SearchResultEntrypointType; use gauntlet_common::model::UiRenderLocation; use gauntlet_common::model::UiTheme; use gauntlet_common::model::UiWidgetId; -use gauntlet_common::model::WindowPositionMode; -use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; -use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; -use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiProxy; use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiRequestData; use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiResponseData; use gauntlet_common::scenario_convert::ui_render_location_from_scenario; @@ -33,10 +27,8 @@ use gauntlet_server::plugins::ApplicationManager; use gauntlet_server::plugins::settings::global_shortcut::GlobalShortcutAction; use gauntlet_server::plugins::settings::global_shortcut::GlobalShortcutPressedEvent; use gauntlet_server::plugins::settings::global_shortcut::register_listener; -use gauntlet_server::rpc::run_grpc_server; use gauntlet_utils::channel::RequestError; use gauntlet_utils::channel::Responder; -use gauntlet_utils::channel::channel; use iced::Event; use iced::Length; use iced::Renderer; @@ -47,9 +39,7 @@ use iced::advanced::graphics::core::SmolStr; use iced::alignment::Horizontal; use iced::alignment::Vertical; use iced::event; -use iced::futures::SinkExt; use iced::futures::StreamExt; -use iced::futures::channel::mpsc; use iced::keyboard; use iced::keyboard::Key; use iced::keyboard::Modifiers; @@ -69,7 +59,6 @@ use iced::widget::text_input::focus; use iced::window; use iced::window::Screenshot; use iced_fonts::BOOTSTRAP_FONT_BYTES; -use tokio::sync::RwLock as TokioRwLock; use crate::model::UiViewEvent; use crate::ui::search_list::search_list; @@ -91,14 +80,14 @@ mod theme; mod widget; mod widget_container; +mod server; mod windows; pub use theme::GauntletComplexTheme; -#[cfg(target_os = "linux")] -use windows::x11_focus::listen_on_x11_active_window_change; use crate::ui::custom_widgets::loading_bar::LoadingBar; use crate::ui::scroll_handle::ScrollHandle; +use crate::ui::server::handle_server_message; use crate::ui::state::ErrorViewData; use crate::ui::state::Focus; use crate::ui::state::GlobalState; @@ -111,29 +100,19 @@ use crate::ui::widget::action_panel::ActionPanelItem; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::root::render_root; use crate::ui::windows::WindowActionMsg; +use crate::ui::windows::WindowState; use crate::ui::windows::create_window; -use crate::ui::windows::hide_window; -use crate::ui::windows::hud::show_hud_window; -use crate::ui::windows::show_window; +#[cfg(target_os = "linux")] +use crate::ui::windows::x11_focus::x11_linux_focus_change_subscription; pub struct AppModel { // logic application_manager: Arc, global_hotkey_manager: GlobalHotKeyManager, - main_window_id: window::Id, - focused: bool, - opened: bool, - #[cfg(target_os = "linux")] - wayland: bool, #[cfg(any(target_os = "macos", target_os = "windows"))] _tray_icon: tray_icon::TrayIcon, theme: GauntletComplexTheme, - window_position_mode: WindowPositionMode, - close_on_unfocus: bool, - window_position_file: Option, - #[cfg(target_os = "linux")] - x11_active_window: Option, - pending_window_state_reset: bool, + window: WindowState, // ephemeral state prompt: String, @@ -237,7 +216,7 @@ pub enum AppMsg { screenshot: Screenshot, }, ShowBackendError(RequestError), - ClosePluginView(PluginId), + ClosePluginView, OpenPluginView(PluginId, EntrypointId), InlineViewShortcuts { shortcuts: HashMap>, @@ -302,6 +281,11 @@ pub enum AppMsg { responder: Arc>>>, }, WindowAction(WindowActionMsg), + ResetWindowState, + ResetMainWindowScroll, + SetHudDisplay { + display: String, + }, } pub fn run(minimized: bool) { @@ -371,28 +355,11 @@ fn run_wayland(minimized: bool) -> anyhow::Result<()> { } fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, Task) { - let (frontend_sender, frontend_receiver) = channel::(); - let (server_grpc_sender, server_grpc_receiver) = channel::(); - - let application_manager = ApplicationManager::create(frontend_sender).expect("Unable to setup application manager"); - let global_hotkey_manager = GlobalHotKeyManager::new().expect("Unable to setup shortcut manager"); - let grpc_api = ServerGrpcApiProxy::new(server_grpc_sender); - - let frontend_receiver = Arc::new(TokioRwLock::new(frontend_receiver)); - let server_grpc_receiver = Arc::new(TokioRwLock::new(server_grpc_receiver)); - let application_manager = Arc::new(application_manager); - - tokio::spawn(async move { run_grpc_server(grpc_api).await }); - - let setup_data = application_manager - .setup(&global_hotkey_manager) - .expect("Unable to setup"); + let (application_manager, global_hotkey_manager, setup_data, setup_task) = server::setup(); let theme = GauntletComplexTheme::new(setup_data.theme); GauntletComplexTheme::set_global(theme.clone()); - let mut tasks: Vec> = vec![]; - let (main_window_id, open_task) = create_window( #[cfg(target_os = "linux")] wayland, @@ -400,36 +367,9 @@ fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, setup_data.window_position_file.as_ref(), ); + let mut tasks: Vec> = vec![]; tasks.push(open_task.map(AppMsg::WindowAction)); - - tasks.push(Task::stream(stream::channel(10, |mut sender| { - async move { - let mut frontend_receiver = frontend_receiver.write().await; - - loop { - let (request_data, responder) = frontend_receiver.recv().await; - - request_loop(request_data, &mut sender, responder).await; - } - } - }))); - - tasks.push(Task::stream(stream::channel(10, |mut sender: mpsc::Sender| { - async move { - let mut server_grpc_receiver = server_grpc_receiver.write().await; - - loop { - let (request_data, responder) = server_grpc_receiver.recv().await; - - let app_msg = AppMsg::HandleServerRequest { - request_data: Arc::new(request_data), - responder: Arc::new(Mutex::new(Some(responder))), - }; - - let _ = sender.send(app_msg).await; - } - } - }))); + tasks.push(setup_task); let mut client_context = ClientContext::new(); @@ -439,25 +379,25 @@ fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, GlobalState::new(text_input::Id::unique()) }; + let window = WindowState::new( + main_window_id, + minimized, + setup_data.window_position_file, + setup_data.close_on_unfocus, + setup_data.window_position_mode, + #[cfg(target_os = "linux")] + wayland, + ); + ( AppModel { // logic application_manager, global_hotkey_manager, - main_window_id, - focused: false, - opened: !minimized, - #[cfg(target_os = "linux")] - wayland, #[cfg(any(target_os = "macos", target_os = "windows"))] _tray_icon: sys_tray::create_tray(), theme, - window_position_mode: setup_data.window_position_mode, - close_on_unfocus: setup_data.close_on_unfocus, - window_position_file: setup_data.window_position_file, - #[cfg(target_os = "linux")] - x11_active_window: None, - pending_window_state_reset: false, + window, // ephemeral state prompt: "".to_string(), @@ -576,7 +516,7 @@ fn scenario_runner_global_state(tasks: &mut Vec>, client_context: & } fn title(state: &AppModel, window: window::Id) -> String { - if window == state.main_window_id { + if window == state.window.main_window_id { "Gauntlet".to_owned() } else { "Gauntlet HUD".to_owned() @@ -641,14 +581,19 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { AppMsg::RunCommand { plugin_id, entrypoint_id, - } => Task::batch([state.hide_window(true), state.run_command(plugin_id, entrypoint_id)]), + } => { + Task::batch([ + Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)), + state.run_command(plugin_id, entrypoint_id), + ]) + } AppMsg::RunGeneratedEntrypoint { plugin_id, entrypoint_id, action_index, } => { Task::batch([ - state.hide_window(true), + Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)), state.run_generated_entrypoint(plugin_id, entrypoint_id, action_index), ]) } @@ -663,7 +608,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { match render_location { UiRenderLocation::InlineView => { Task::batch([ - state.hide_window(true), + Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)), Task::done(AppMsg::WidgetEvent { widget_event, plugin_id, @@ -851,7 +796,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } AppMsg::IcedEvent(window_id, Event::Keyboard(event)) => { - if window_id != state.main_window_id { + if window_id != state.window.main_window_id { return Task::none(); } @@ -941,47 +886,13 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } AppMsg::IcedEvent(window_id, Event::Window(window::Event::Focused)) => { - if !state.close_on_unfocus { - return Task::none(); - } - - if window_id != state.main_window_id { - return Task::none(); - } - - state.on_focused() + state.window.handle_focused_event(window_id) } AppMsg::IcedEvent(window_id, Event::Window(window::Event::Unfocused)) => { - if !state.close_on_unfocus { - return Task::none(); - } - - if window_id != state.main_window_id { - return Task::none(); - } - - #[cfg(target_os = "linux")] - if state.wayland { - state.hide_window(true) - } else { - // x11 uses separate mechanism based on _NET_ACTIVE_WINDOW property - Task::none() - } - - #[cfg(not(target_os = "linux"))] - state.on_unfocused() + state.window.handle_unfocused_event(window_id) } AppMsg::IcedEvent(window_id, Event::Window(window::Event::Moved(point))) => { - if window_id != state.main_window_id { - return Task::none(); - } - - if let Some(window_position_file) = &state.window_position_file { - let _ = fs::create_dir_all(window_position_file.parent().unwrap()); - let _ = fs::write(&window_position_file, format!("{}:{}", point.x, point.y)); - } - - Task::none() + state.window.handle_move_event(window_id, point) } AppMsg::IcedEvent(_, _) => Task::none(), AppMsg::WidgetEvent { @@ -1072,7 +983,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { fs::create_dir_all(Path::new(&save_path).parent().expect("no parent?")) .expect("unable to create scenario out directories"); - window::screenshot(state.main_window_id).map(move |screenshot| { + window::screenshot(state.window.main_window_id).map(move |screenshot| { AppMsg::ScreenshotDone { save_path: save_path.clone(), screenshot, @@ -1272,7 +1183,13 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { }) } AppMsg::OpenPluginView(plugin_id, entrypoint_id) => state.open_plugin_view(plugin_id, entrypoint_id), - AppMsg::ClosePluginView(plugin_id) => state.close_plugin_view(plugin_id), + AppMsg::ClosePluginView => { + if let GlobalState::PluginView { plugin_view_data, .. } = &state.global_state { + state.close_plugin_view(plugin_view_data.plugin_id.clone()) + } else { + Task::none() + } + } AppMsg::InlineViewShortcuts { shortcuts } => { state.client_context.set_inline_view_shortcuts(shortcuts); @@ -1335,49 +1252,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } AppMsg::FocusPluginViewSearchBar { widget_id } => state.client_context.focus_search_bar(widget_id), - AppMsg::WindowAction(action) => { - match action { - #[cfg(target_os = "linux")] - WindowActionMsg::LayerShell(_) => { - // handled by library - Task::none() - } - WindowActionMsg::SetWindowPositionMode { mode } => { - state.window_position_mode = mode; - - Task::none() - } - #[cfg(target_os = "linux")] - WindowActionMsg::X11ActiveWindowChanged { window, wm_name } => { - if state.x11_active_window != Some(window) { - state.x11_active_window = Some(window); - if let Some(wm_name) = &wm_name { - if wm_name != "gauntlet" { - Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)) - } else { - Task::none() - } - } else { - Task::none() - } - } else { - Task::none() - } - } - WindowActionMsg::ToggleWindow => state.toggle_window(), - WindowActionMsg::ShowWindow => state.show_window(), - WindowActionMsg::HideWindow => state.hide_window(true), - WindowActionMsg::ShowHud { display } => { - state.hud_display = Some(display); - - show_hud_window( - #[cfg(target_os = "linux")] - state.wayland, - ) - .map(AppMsg::WindowAction) - } - } - } + AppMsg::WindowAction(action) => state.window.handle_action(action), AppMsg::ClearInlineView { plugin_id } => { state.client_context.clear_inline_view(&plugin_id); @@ -1471,11 +1346,27 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { request_data, responder, } => handle_server_message(state, request_data, responder), + AppMsg::ResetWindowState => state.reset_window_state(), + AppMsg::ResetMainWindowScroll => { + if let GlobalState::MainView { + focused_search_result, .. + } = &state.global_state + { + focused_search_result.scroll_to(0) + } else { + Task::none() + } + } + AppMsg::SetHudDisplay { display } => { + state.hud_display = Some(display); + + Task::none() + } } } fn view(state: &AppModel, window: window::Id) -> Element<'_, AppMsg> { - if window != state.main_window_id { + if window != state.window.main_window_id { view_hud(state) } else { view_main(state) @@ -2051,112 +1942,14 @@ fn subscription(#[allow(unused)] state: &AppModel) -> Subscription { ]; #[cfg(target_os = "linux")] - if !state.wayland { - let subscription = Subscription::run(|| { - stream::channel(100, async move |sender| { - let handle = tokio::runtime::Handle::current(); - - let err = tokio::task::spawn_blocking(|| listen_on_x11_active_window_change(sender, handle)).await; - - if let Err(err) = err { - tracing::error!("error occurred when listening on x11 events: {:?}", err); - } - }) - }); - - subscriptions.push(subscription) + if !state.window.wayland { + subscriptions.push(x11_linux_focus_change_subscription()) } Subscription::batch(subscriptions) } impl AppModel { - fn on_focused(&mut self) -> Task { - self.focused = true; - Task::none() - } - - #[allow(unused)] - fn on_unfocused(&mut self) -> Task { - // for some reason (on both macOS and linux x11 but x11 now uses separate impl) duplicate Unfocused fires right before Focus event - if self.focused { - self.hide_window(true) - } else { - Task::none() - } - } - - fn toggle_window(&mut self) -> Task { - if self.opened { - self.hide_window(false) - } else { - self.show_window() - } - } - - fn hide_window(&mut self, reset_state: bool) -> Task { - if !self.opened { - return Task::none(); - } - - self.focused = false; - self.opened = false; - - let mut commands = vec![]; - - self.pending_window_state_reset = reset_state; - - commands.push( - hide_window( - #[cfg(target_os = "linux")] - self.wayland, - self.main_window_id, - ) - .map(AppMsg::WindowAction), - ); - - match &self.global_state { - GlobalState::PluginView { - plugin_view_data: PluginViewData { plugin_id, .. }, - .. - } => { - if reset_state { - commands.push(self.close_plugin_view(plugin_id.clone())); - } - } - GlobalState::MainView { - focused_search_result, .. - } => { - commands.push(focused_search_result.scroll_to(0)); - } - GlobalState::ErrorView { .. } => {} - GlobalState::PendingPluginView { .. } => {} - } - - if self.pending_window_state_reset { - commands.push(self.reset_window_state()); - } - - Task::batch(commands) - } - - fn show_window(&mut self) -> Task { - if self.opened { - return Task::none(); - } - - self.opened = true; - - show_window( - self.main_window_id, - #[cfg(target_os = "linux")] - self.wayland, - #[cfg(target_os = "macos")] - self.window_position_mode, - ) - .map(AppMsg::WindowAction) - } - fn reset_window_state(&mut self) -> Task { self.prompt = "".to_string(); @@ -2581,399 +2374,3 @@ impl AppModel { focus(search_field_id.clone()) } } - -async fn request_loop( - request_data: FrontendApiRequestData, - sender: &mut mpsc::Sender, - responder: Responder, -) { - let app_msg = { - match request_data { - FrontendApiRequestData::ReplaceView { - plugin_id, - plugin_name, - entrypoint_id, - entrypoint_name, - render_location, - top_level_view, - container, - data: images, - } => { - responder.respond(Ok(FrontendApiResponseData::ReplaceView { data: () })); - - AppMsg::RenderPluginUI { - plugin_id, - plugin_name, - entrypoint_id, - entrypoint_name, - render_location, - top_level_view, - container: Arc::new(container), - data: images, - } - } - FrontendApiRequestData::ClearInlineView { plugin_id } => { - responder.respond(Ok(FrontendApiResponseData::ClearInlineView { data: () })); - - AppMsg::ClearInlineView { plugin_id } - } - FrontendApiRequestData::ShowWindow {} => { - responder.respond(Ok(FrontendApiResponseData::ShowWindow { data: () })); - - AppMsg::WindowAction(WindowActionMsg::ShowWindow) - } - FrontendApiRequestData::HideWindow {} => { - responder.respond(Ok(FrontendApiResponseData::HideWindow { data: () })); - - AppMsg::WindowAction(WindowActionMsg::HideWindow) - } - FrontendApiRequestData::ShowPreferenceRequiredView { - plugin_id, - entrypoint_id, - plugin_preferences_required, - entrypoint_preferences_required, - } => { - responder.respond(Ok(FrontendApiResponseData::ShowPreferenceRequiredView { data: () })); - - AppMsg::ShowPreferenceRequiredView { - plugin_id, - entrypoint_id, - plugin_preferences_required, - entrypoint_preferences_required, - } - } - FrontendApiRequestData::ShowPluginErrorView { - plugin_id, - entrypoint_id, - render_location: _, - } => { - responder.respond(Ok(FrontendApiResponseData::ShowPluginErrorView { data: () })); - - AppMsg::ShowPluginErrorView { - plugin_id, - entrypoint_id, - } - } - FrontendApiRequestData::RequestSearchResultsUpdate {} => { - responder.respond(Ok(FrontendApiResponseData::RequestSearchResultsUpdate { data: () })); - - AppMsg::UpdateSearchResults - } - FrontendApiRequestData::ShowHud { display } => { - responder.respond(Ok(FrontendApiResponseData::ShowHud { data: () })); - - AppMsg::WindowAction(WindowActionMsg::ShowHud { display }) - } - FrontendApiRequestData::UpdateLoadingBar { - plugin_id, - entrypoint_id, - show, - } => { - responder.respond(Ok(FrontendApiResponseData::UpdateLoadingBar { data: () })); - - AppMsg::UpdateLoadingBar { - plugin_id, - entrypoint_id, - show, - } - } - FrontendApiRequestData::SetTheme { theme } => { - responder.respond(Ok(FrontendApiResponseData::SetTheme { data: () })); - - AppMsg::SetTheme { theme } - } - FrontendApiRequestData::SetWindowPositionMode { mode } => { - responder.respond(Ok(FrontendApiResponseData::SetWindowPositionMode { data: () })); - - AppMsg::WindowAction(WindowActionMsg::SetWindowPositionMode { mode }) - } - FrontendApiRequestData::OpenPluginView { - plugin_id, - entrypoint_id, - } => { - responder.respond(Ok(FrontendApiResponseData::OpenPluginView { data: () })); - - AppMsg::ShowNewView { - plugin_id, - entrypoint_id, - } - } - FrontendApiRequestData::OpenGeneratedPluginView { - plugin_id, - entrypoint_id, - action_index, - } => { - responder.respond(Ok(FrontendApiResponseData::OpenGeneratedPluginView { data: () })); - - AppMsg::ShowNewGeneratedView { - plugin_id, - entrypoint_id, - action_index, - } - } - } - }; - - let _ = sender.send(app_msg).await; -} - -fn handle_server_message( - state: &mut AppModel, - request_data: Arc, - responder: Arc>>>, -) -> Task { - let responder = responder - .lock() - .expect("lock is poisoned") - .take() - .expect("there should always be a responder here"); - - match request_data.deref() { - ServerGrpcApiRequestData::ShowWindow {} => { - responder.respond(Ok(ServerGrpcApiResponseData::ShowWindow { data: () })); - - state.show_window() - } - ServerGrpcApiRequestData::ShowSettingsWindow {} => { - responder.respond(Ok(ServerGrpcApiResponseData::ShowSettingsWindow { data: () })); - - state.application_manager.handle_open_settings_window(); - - Task::none() - } - ServerGrpcApiRequestData::RunAction { - plugin_id, - entrypoint_id, - action_id, - } => { - let application_manager = state.application_manager.clone(); - let plugin_id = plugin_id.clone(); - let entrypoint_id = entrypoint_id.clone(); - let action_id = action_id.clone(); - - Task::future(async move { - let result = application_manager - .run_action(plugin_id, entrypoint_id, action_id) - .await - .map(|data| ServerGrpcApiResponseData::RunAction { data }); - - responder.respond(result); - - AppMsg::Noop - }) - } - ServerGrpcApiRequestData::SaveLocalPlugin { path } => { - let result = state - .application_manager - .save_local_plugin(&path) - .map(|data| ServerGrpcApiResponseData::SaveLocalPlugin { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::Plugins {} => { - let result = state - .application_manager - .plugins() - .map(|data| ServerGrpcApiResponseData::Plugins { data }); - - responder.respond(result.into()); - - Task::none() - } - ServerGrpcApiRequestData::SetPluginState { plugin_id, enabled } => { - let result = state - .application_manager - .set_plugin_state(plugin_id.clone(), *enabled) - .map(|data| ServerGrpcApiResponseData::SetPluginState { data }); - - responder.respond(result.into()); - - Task::none() - } - ServerGrpcApiRequestData::SetEntrypointState { - plugin_id, - entrypoint_id, - enabled, - } => { - let result = state - .application_manager - .set_entrypoint_state(plugin_id.clone(), entrypoint_id.clone(), *enabled) - .map(|data| ServerGrpcApiResponseData::SetEntrypointState { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetGlobalShortcut { shortcut } => { - let result = state - .application_manager - .set_global_shortcut(&state.global_hotkey_manager, shortcut.clone()); - - responder.respond(Ok(ServerGrpcApiResponseData::SetGlobalShortcut { data: result })); - - Task::none() - } - ServerGrpcApiRequestData::GetGlobalShortcut {} => { - let result = state - .application_manager - .get_global_shortcut() - .map(|data| ServerGrpcApiResponseData::GetGlobalShortcut { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetGlobalEntrypointShortcut { - plugin_id, - entrypoint_id, - shortcut, - } => { - let result = state - .application_manager - .set_global_entrypoint_shortcut( - &state.global_hotkey_manager, - plugin_id.clone(), - entrypoint_id.clone(), - shortcut.clone(), - ) - .map(|data| ServerGrpcApiResponseData::SetGlobalEntrypointShortcut { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::GetGlobalEntrypointShortcuts {} => { - let result = state - .application_manager - .get_global_entrypoint_shortcut() - .map(|data| ServerGrpcApiResponseData::GetGlobalEntrypointShortcuts { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetEntrypointSearchAlias { - plugin_id, - entrypoint_id, - alias, - } => { - let result = state - .application_manager - .set_entrypoint_search_alias(plugin_id.clone(), entrypoint_id.clone(), alias.clone()) - .map(|data| ServerGrpcApiResponseData::SetEntrypointSearchAlias { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::GetEntrypointSearchAliases {} => { - let result = state - .application_manager - .get_entrypoint_search_aliases() - .map(|data| ServerGrpcApiResponseData::GetEntrypointSearchAliases { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetTheme { theme } => { - let application_manager = state.application_manager.clone(); - let theme = theme.clone(); - - Task::future(async move { - let result = application_manager - .set_theme(theme) - .await - .map(|data| ServerGrpcApiResponseData::SetTheme { data }); - - responder.respond(result); - - AppMsg::Noop - }) - } - ServerGrpcApiRequestData::GetTheme {} => { - let result = state - .application_manager - .get_theme() - .map(|data| ServerGrpcApiResponseData::GetTheme { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetWindowPositionMode { mode } => { - let application_manager = state.application_manager.clone(); - let mode = mode.clone(); - - Task::future(async move { - let result = application_manager - .set_window_position_mode(mode) - .await - .map(|data| ServerGrpcApiResponseData::SetWindowPositionMode { data }); - - responder.respond(result); - - AppMsg::Noop - }) - } - ServerGrpcApiRequestData::GetWindowPositionMode {} => { - let result = state - .application_manager - .get_window_position_mode() - .map(|data| ServerGrpcApiResponseData::GetWindowPositionMode { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetPreferenceValue { - plugin_id, - entrypoint_id, - preference_id, - preference_value, - } => { - let result = state - .application_manager - .set_preference_value( - plugin_id.clone(), - entrypoint_id.clone(), - preference_id.clone(), - preference_value.clone(), - ) - .map(|data| ServerGrpcApiResponseData::SetPreferenceValue { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::DownloadPlugin { plugin_id } => { - let result = state - .application_manager - .download_plugin(plugin_id.clone()) - .map(|data| ServerGrpcApiResponseData::DownloadPlugin { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::DownloadStatus {} => { - let data = state.application_manager.download_status(); - - responder.respond(Ok(ServerGrpcApiResponseData::DownloadStatus { data })); - - Task::none() - } - ServerGrpcApiRequestData::RemovePlugin { plugin_id } => { - let result = state - .application_manager - .remove_plugin(plugin_id.clone()) - .map(|data| ServerGrpcApiResponseData::RemovePlugin { data }); - - responder.respond(result); - - Task::none() - } - } -} diff --git a/rust/client/src/ui/server.rs b/rust/client/src/ui/server.rs new file mode 100644 index 0000000..7cbeaab --- /dev/null +++ b/rust/client/src/ui/server.rs @@ -0,0 +1,477 @@ +use std::ops::Deref; +use std::sync::Arc; +use std::sync::Mutex; + +use gauntlet_common::model::UiSetupData; +use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; +use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; +use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiProxy; +use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiRequestData; +use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiResponseData; +use gauntlet_server::global_hotkey::GlobalHotKeyManager; +use gauntlet_server::plugins::ApplicationManager; +use gauntlet_server::rpc::run_grpc_server; +use gauntlet_utils::channel::Responder; +use gauntlet_utils::channel::channel; +use iced::Task; +use iced::futures::SinkExt; +use iced::futures::channel::mpsc; +use iced::stream; +use tokio::sync::RwLock as TokioRwLock; + +use crate::ui::AppModel; +use crate::ui::AppMsg; +use crate::ui::windows::WindowActionMsg; + +pub fn setup() -> (Arc, GlobalHotKeyManager, UiSetupData, Task) { + let (frontend_sender, frontend_receiver) = channel::(); + let (server_grpc_sender, server_grpc_receiver) = channel::(); + + let application_manager = ApplicationManager::create(frontend_sender).expect("Unable to setup application manager"); + let global_hotkey_manager = GlobalHotKeyManager::new().expect("Unable to setup shortcut manager"); + let grpc_api = ServerGrpcApiProxy::new(server_grpc_sender); + + let frontend_receiver = Arc::new(TokioRwLock::new(frontend_receiver)); + let server_grpc_receiver = Arc::new(TokioRwLock::new(server_grpc_receiver)); + let application_manager = Arc::new(application_manager); + + tokio::spawn(async move { run_grpc_server(grpc_api).await }); + + let setup_data = application_manager + .setup(&global_hotkey_manager) + .expect("Unable to setup"); + + let mut tasks = vec![]; + + tasks.push(Task::stream(stream::channel(10, |mut sender| { + async move { + let mut frontend_receiver = frontend_receiver.write().await; + + loop { + let (request_data, responder) = frontend_receiver.recv().await; + + request_loop(request_data, &mut sender, responder).await; + } + } + }))); + + tasks.push(Task::stream(stream::channel(10, |mut sender: mpsc::Sender| { + async move { + let mut server_grpc_receiver = server_grpc_receiver.write().await; + + loop { + let (request_data, responder) = server_grpc_receiver.recv().await; + + let app_msg = AppMsg::HandleServerRequest { + request_data: Arc::new(request_data), + responder: Arc::new(Mutex::new(Some(responder))), + }; + + let _ = sender.send(app_msg).await; + } + } + }))); + + ( + application_manager, + global_hotkey_manager, + setup_data, + Task::batch(tasks), + ) +} + +async fn request_loop( + request_data: FrontendApiRequestData, + sender: &mut mpsc::Sender, + responder: Responder, +) { + let app_msg = { + match request_data { + FrontendApiRequestData::ReplaceView { + plugin_id, + plugin_name, + entrypoint_id, + entrypoint_name, + render_location, + top_level_view, + container, + data: images, + } => { + responder.respond(Ok(FrontendApiResponseData::ReplaceView { data: () })); + + AppMsg::RenderPluginUI { + plugin_id, + plugin_name, + entrypoint_id, + entrypoint_name, + render_location, + top_level_view, + container: Arc::new(container), + data: images, + } + } + FrontendApiRequestData::ClearInlineView { plugin_id } => { + responder.respond(Ok(FrontendApiResponseData::ClearInlineView { data: () })); + + AppMsg::ClearInlineView { plugin_id } + } + FrontendApiRequestData::ShowWindow {} => { + responder.respond(Ok(FrontendApiResponseData::ShowWindow { data: () })); + + AppMsg::WindowAction(WindowActionMsg::ShowWindow) + } + FrontendApiRequestData::HideWindow {} => { + responder.respond(Ok(FrontendApiResponseData::HideWindow { data: () })); + + AppMsg::WindowAction(WindowActionMsg::HideWindow) + } + FrontendApiRequestData::ShowPreferenceRequiredView { + plugin_id, + entrypoint_id, + plugin_preferences_required, + entrypoint_preferences_required, + } => { + responder.respond(Ok(FrontendApiResponseData::ShowPreferenceRequiredView { data: () })); + + AppMsg::ShowPreferenceRequiredView { + plugin_id, + entrypoint_id, + plugin_preferences_required, + entrypoint_preferences_required, + } + } + FrontendApiRequestData::ShowPluginErrorView { + plugin_id, + entrypoint_id, + render_location: _, + } => { + responder.respond(Ok(FrontendApiResponseData::ShowPluginErrorView { data: () })); + + AppMsg::ShowPluginErrorView { + plugin_id, + entrypoint_id, + } + } + FrontendApiRequestData::RequestSearchResultsUpdate {} => { + responder.respond(Ok(FrontendApiResponseData::RequestSearchResultsUpdate { data: () })); + + AppMsg::UpdateSearchResults + } + FrontendApiRequestData::ShowHud { display } => { + responder.respond(Ok(FrontendApiResponseData::ShowHud { data: () })); + + AppMsg::WindowAction(WindowActionMsg::ShowHud { display }) + } + FrontendApiRequestData::UpdateLoadingBar { + plugin_id, + entrypoint_id, + show, + } => { + responder.respond(Ok(FrontendApiResponseData::UpdateLoadingBar { data: () })); + + AppMsg::UpdateLoadingBar { + plugin_id, + entrypoint_id, + show, + } + } + FrontendApiRequestData::SetTheme { theme } => { + responder.respond(Ok(FrontendApiResponseData::SetTheme { data: () })); + + AppMsg::SetTheme { theme } + } + FrontendApiRequestData::SetWindowPositionMode { mode } => { + responder.respond(Ok(FrontendApiResponseData::SetWindowPositionMode { data: () })); + + AppMsg::WindowAction(WindowActionMsg::SetWindowPositionMode { mode }) + } + FrontendApiRequestData::OpenPluginView { + plugin_id, + entrypoint_id, + } => { + responder.respond(Ok(FrontendApiResponseData::OpenPluginView { data: () })); + + AppMsg::ShowNewView { + plugin_id, + entrypoint_id, + } + } + FrontendApiRequestData::OpenGeneratedPluginView { + plugin_id, + entrypoint_id, + action_index, + } => { + responder.respond(Ok(FrontendApiResponseData::OpenGeneratedPluginView { data: () })); + + AppMsg::ShowNewGeneratedView { + plugin_id, + entrypoint_id, + action_index, + } + } + } + }; + + let _ = sender.send(app_msg).await; +} + +pub fn handle_server_message( + state: &mut AppModel, + request_data: Arc, + responder: Arc>>>, +) -> Task { + let responder = responder + .lock() + .expect("lock is poisoned") + .take() + .expect("there should always be a responder here"); + + match request_data.deref() { + ServerGrpcApiRequestData::ShowWindow {} => { + responder.respond(Ok(ServerGrpcApiResponseData::ShowWindow { data: () })); + + Task::done(AppMsg::WindowAction(WindowActionMsg::ShowWindow)) + } + ServerGrpcApiRequestData::ShowSettingsWindow {} => { + responder.respond(Ok(ServerGrpcApiResponseData::ShowSettingsWindow { data: () })); + + state.application_manager.handle_open_settings_window(); + + Task::none() + } + ServerGrpcApiRequestData::RunAction { + plugin_id, + entrypoint_id, + action_id, + } => { + let application_manager = state.application_manager.clone(); + let plugin_id = plugin_id.clone(); + let entrypoint_id = entrypoint_id.clone(); + let action_id = action_id.clone(); + + Task::future(async move { + let result = application_manager + .run_action(plugin_id, entrypoint_id, action_id) + .await + .map(|data| ServerGrpcApiResponseData::RunAction { data }); + + responder.respond(result); + + AppMsg::Noop + }) + } + ServerGrpcApiRequestData::SaveLocalPlugin { path } => { + let result = state + .application_manager + .save_local_plugin(&path) + .map(|data| ServerGrpcApiResponseData::SaveLocalPlugin { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::Plugins {} => { + let result = state + .application_manager + .plugins() + .map(|data| ServerGrpcApiResponseData::Plugins { data }); + + responder.respond(result.into()); + + Task::none() + } + ServerGrpcApiRequestData::SetPluginState { plugin_id, enabled } => { + let result = state + .application_manager + .set_plugin_state(plugin_id.clone(), *enabled) + .map(|data| ServerGrpcApiResponseData::SetPluginState { data }); + + responder.respond(result.into()); + + Task::none() + } + ServerGrpcApiRequestData::SetEntrypointState { + plugin_id, + entrypoint_id, + enabled, + } => { + let result = state + .application_manager + .set_entrypoint_state(plugin_id.clone(), entrypoint_id.clone(), *enabled) + .map(|data| ServerGrpcApiResponseData::SetEntrypointState { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetGlobalShortcut { shortcut } => { + let result = state + .application_manager + .set_global_shortcut(&state.global_hotkey_manager, shortcut.clone()); + + responder.respond(Ok(ServerGrpcApiResponseData::SetGlobalShortcut { data: result })); + + Task::none() + } + ServerGrpcApiRequestData::GetGlobalShortcut {} => { + let result = state + .application_manager + .get_global_shortcut() + .map(|data| ServerGrpcApiResponseData::GetGlobalShortcut { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetGlobalEntrypointShortcut { + plugin_id, + entrypoint_id, + shortcut, + } => { + let result = state + .application_manager + .set_global_entrypoint_shortcut( + &state.global_hotkey_manager, + plugin_id.clone(), + entrypoint_id.clone(), + shortcut.clone(), + ) + .map(|data| ServerGrpcApiResponseData::SetGlobalEntrypointShortcut { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::GetGlobalEntrypointShortcuts {} => { + let result = state + .application_manager + .get_global_entrypoint_shortcut() + .map(|data| ServerGrpcApiResponseData::GetGlobalEntrypointShortcuts { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetEntrypointSearchAlias { + plugin_id, + entrypoint_id, + alias, + } => { + let result = state + .application_manager + .set_entrypoint_search_alias(plugin_id.clone(), entrypoint_id.clone(), alias.clone()) + .map(|data| ServerGrpcApiResponseData::SetEntrypointSearchAlias { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::GetEntrypointSearchAliases {} => { + let result = state + .application_manager + .get_entrypoint_search_aliases() + .map(|data| ServerGrpcApiResponseData::GetEntrypointSearchAliases { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetTheme { theme } => { + let application_manager = state.application_manager.clone(); + let theme = theme.clone(); + + Task::future(async move { + let result = application_manager + .set_theme(theme) + .await + .map(|data| ServerGrpcApiResponseData::SetTheme { data }); + + responder.respond(result); + + AppMsg::Noop + }) + } + ServerGrpcApiRequestData::GetTheme {} => { + let result = state + .application_manager + .get_theme() + .map(|data| ServerGrpcApiResponseData::GetTheme { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetWindowPositionMode { mode } => { + let application_manager = state.application_manager.clone(); + let mode = mode.clone(); + + Task::future(async move { + let result = application_manager + .set_window_position_mode(mode) + .await + .map(|data| ServerGrpcApiResponseData::SetWindowPositionMode { data }); + + responder.respond(result); + + AppMsg::Noop + }) + } + ServerGrpcApiRequestData::GetWindowPositionMode {} => { + let result = state + .application_manager + .get_window_position_mode() + .map(|data| ServerGrpcApiResponseData::GetWindowPositionMode { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::SetPreferenceValue { + plugin_id, + entrypoint_id, + preference_id, + preference_value, + } => { + let result = state + .application_manager + .set_preference_value( + plugin_id.clone(), + entrypoint_id.clone(), + preference_id.clone(), + preference_value.clone(), + ) + .map(|data| ServerGrpcApiResponseData::SetPreferenceValue { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::DownloadPlugin { plugin_id } => { + let result = state + .application_manager + .download_plugin(plugin_id.clone()) + .map(|data| ServerGrpcApiResponseData::DownloadPlugin { data }); + + responder.respond(result); + + Task::none() + } + ServerGrpcApiRequestData::DownloadStatus {} => { + let data = state.application_manager.download_status(); + + responder.respond(Ok(ServerGrpcApiResponseData::DownloadStatus { data })); + + Task::none() + } + ServerGrpcApiRequestData::RemovePlugin { plugin_id } => { + let result = state + .application_manager + .remove_plugin(plugin_id.clone()) + .map(|data| ServerGrpcApiResponseData::RemovePlugin { data }); + + responder.respond(result); + + Task::none() + } + } +} diff --git a/rust/client/src/ui/state/mod.rs b/rust/client/src/ui/state/mod.rs index 2d962ff..365382b 100644 --- a/rust/client/src/ui/state/mod.rs +++ b/rust/client/src/ui/state/mod.rs @@ -312,18 +312,13 @@ impl Focus for GlobalState { match sub_state { PluginViewState::None => { if *top_level_view { - let plugin_id = plugin_id.clone(); - if *close_window_on_esc { Task::batch([ - Task::done(AppMsg::ClosePluginView(plugin_id)), + Task::done(AppMsg::ClosePluginView), Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)), ]) } else { - Task::batch([ - Task::done(AppMsg::ClosePluginView(plugin_id)), - GlobalState::initial(self), - ]) + Task::batch([Task::done(AppMsg::ClosePluginView), GlobalState::initial(self)]) } } else { let plugin_id = plugin_id.clone(); diff --git a/rust/client/src/ui/windows/hud.rs b/rust/client/src/ui/windows/hud.rs index bba5e6c..c35657b 100644 --- a/rust/client/src/ui/windows/hud.rs +++ b/rust/client/src/ui/windows/hud.rs @@ -9,7 +9,6 @@ use iced::window::Position; use iced::window::Settings; use crate::ui::windows::WindowActionMsg; -use crate::ui::windows::layer_shell; const HUD_WINDOW_WIDTH: f32 = 400.0; const HUD_WINDOW_HEIGHT: f32 = 40.0; @@ -51,12 +50,13 @@ fn open_non_wayland() -> Task { window::open(settings) .1 - .then(|id| sleep_for_2_seconds(id)) - .then(|id| window::close(id)) + .then(|id| sleep_for_2_seconds(id).then(|id| window::close(id))) } #[cfg(target_os = "linux")] fn open_wayland() -> Task { + use crate::ui::windows::layer_shell; + let id = window::Id::unique(); let settings = layer_shell_settings(); diff --git a/rust/client/src/ui/windows/mod.rs b/rust/client/src/ui/windows/mod.rs index 634de42..0594d7b 100644 --- a/rust/client/src/ui/windows/mod.rs +++ b/rust/client/src/ui/windows/mod.rs @@ -11,11 +11,209 @@ use iced::window::Mode; use iced::window::Position; use crate::ui::AppMsg; +use crate::ui::windows::hud::show_hud_window; pub mod hud; #[cfg(target_os = "linux")] pub mod x11_focus; +pub struct WindowState { + pub main_window_id: window::Id, + focused: bool, + opened: bool, + #[cfg(target_os = "linux")] + pub wayland: bool, + window_position_mode: WindowPositionMode, + close_on_unfocus: bool, + window_position_file: Option, + #[cfg(target_os = "linux")] + x11_active_window: Option, +} + +impl WindowState { + pub fn new( + main_window_id: window::Id, + minimized: bool, + window_position_file: Option, + close_on_unfocus: bool, + window_position_mode: WindowPositionMode, + #[cfg(target_os = "linux")] wayland: bool, + ) -> WindowState { + Self { + main_window_id, + focused: false, + opened: !minimized, + #[cfg(target_os = "linux")] + wayland, + window_position_mode, + close_on_unfocus, + window_position_file, + #[cfg(target_os = "linux")] + x11_active_window: None, + } + } +} + +impl WindowState { + pub fn handle_action(&mut self, action: WindowActionMsg) -> Task { + match action { + #[cfg(target_os = "linux")] + WindowActionMsg::LayerShell(_) => { + // handled by library + Task::none() + } + WindowActionMsg::SetWindowPositionMode { mode } => { + self.window_position_mode = mode; + + Task::none() + } + #[cfg(target_os = "linux")] + WindowActionMsg::X11ActiveWindowChanged { window, wm_name } => { + if self.x11_active_window != Some(window) { + self.x11_active_window = Some(window); + if let Some(wm_name) = &wm_name { + if wm_name != "gauntlet" { + Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)) + } else { + Task::none() + } + } else { + Task::none() + } + } else { + Task::none() + } + } + WindowActionMsg::ToggleWindow => self.toggle_window(), + WindowActionMsg::ShowWindow => self.show_window(), + WindowActionMsg::HideWindow => self.hide_window(true), + WindowActionMsg::ShowHud { display } => { + let show_hud = show_hud_window( + #[cfg(target_os = "linux")] + self.wayland, + ) + .map(AppMsg::WindowAction); + + Task::batch([Task::done(AppMsg::SetHudDisplay { display }), show_hud]) + } + } + } + pub fn handle_unfocused_event(&mut self, window_id: window::Id) -> Task { + if !self.close_on_unfocus { + return Task::none(); + } + + if window_id != self.main_window_id { + return Task::none(); + } + + #[cfg(target_os = "linux")] + if self.wayland { + self.hide_window(true) + } else { + // x11 uses separate mechanism based on _NET_ACTIVE_WINDOW property + Task::none() + } + + #[cfg(not(target_os = "linux"))] + self.on_unfocused() + } + + pub fn handle_focused_event(&mut self, window_id: window::Id) -> Task { + if !self.close_on_unfocus { + return Task::none(); + } + + if window_id != self.main_window_id { + return Task::none(); + } + + self.on_focused() + } + + pub fn handle_move_event(&mut self, window_id: window::Id, point: Point) -> Task { + if window_id != self.main_window_id { + return Task::none(); + } + + if let Some(window_position_file) = &self.window_position_file { + let _ = fs::create_dir_all(window_position_file.parent().unwrap()); + let _ = fs::write(&window_position_file, format!("{}:{}", point.x, point.y)); + } + + Task::none() + } + + fn on_focused(&mut self) -> Task { + self.focused = true; + Task::none() + } + + #[allow(unused)] + fn on_unfocused(&mut self) -> Task { + // for some reason (on both macOS and linux x11 but x11 now uses separate impl) duplicate Unfocused fires right before Focus event + if self.focused { + self.hide_window(true) + } else { + Task::none() + } + } + + fn toggle_window(&mut self) -> Task { + if self.opened { + self.hide_window(false) + } else { + self.show_window() + } + } + + fn hide_window(&mut self, reset_state: bool) -> Task { + if !self.opened { + return Task::none(); + } + + self.focused = false; + self.opened = false; + + let mut commands = vec![]; + + commands.push( + hide_window( + #[cfg(target_os = "linux")] + self.wayland, + self.main_window_id, + ) + .map(AppMsg::WindowAction), + ); + + if reset_state { + commands.push(Task::done(AppMsg::ClosePluginView)); + commands.push(Task::done(AppMsg::ResetWindowState)); + } + + commands.push(Task::done(AppMsg::ResetMainWindowScroll)); + + Task::batch(commands) + } + + fn show_window(&mut self) -> Task { + if self.opened { + return Task::none(); + } + + self.opened = true; + + show_window( + self.main_window_id, + #[cfg(target_os = "linux")] + self.wayland, + #[cfg(target_os = "macos")] + &self.window_position_mode, + ) + .map(AppMsg::WindowAction) + } +} + #[derive(Debug, Clone)] pub enum WindowActionMsg { #[cfg(target_os = "linux")] @@ -129,7 +327,7 @@ pub fn create_window( pub fn show_window( main_window_id: window::Id, #[cfg(target_os = "linux")] wayland: bool, - #[cfg(target_os = "macos")] window_position_mode: WindowPositionMode, + #[cfg(target_os = "macos")] window_position_mode: &WindowPositionMode, ) -> Task { #[cfg(target_os = "linux")] let open_task = if wayland { diff --git a/rust/client/src/ui/windows/x11_focus.rs b/rust/client/src/ui/windows/x11_focus.rs index 8fff8af..91a1572 100644 --- a/rust/client/src/ui/windows/x11_focus.rs +++ b/rust/client/src/ui/windows/x11_focus.rs @@ -1,8 +1,10 @@ use std::convert::Infallible; use anyhow::anyhow; +use iced::Subscription; use iced::futures::SinkExt; use iced::futures::channel::mpsc::Sender; +use iced::stream; use tokio::runtime::Handle; use x11rb::connection::Connection; use x11rb::properties::WmClass; @@ -16,7 +18,21 @@ use x11rb::rust_connection::RustConnection; use crate::ui::AppMsg; use crate::ui::windows::WindowActionMsg; -pub fn listen_on_x11_active_window_change(sender: Sender, handle: Handle) -> anyhow::Result { +pub fn x11_linux_focus_change_subscription() -> Subscription { + Subscription::run(|| { + stream::channel(100, async move |sender| { + let handle = Handle::current(); + + let err = tokio::task::spawn_blocking(|| listen_on_x11_active_window_change(sender, handle)).await; + + if let Err(err) = err { + tracing::error!("error occurred when listening on x11 events: {:?}", err); + } + }) + }) +} + +fn listen_on_x11_active_window_change(sender: Sender, handle: Handle) -> anyhow::Result { let (conn, screen_num) = RustConnection::connect(None)?; let screen = &conn.setup().roots[screen_num]; let atoms = atoms::Atoms::new(&conn)?.reply()?; From f529d061ff34b5a1a390f6a47d1d711d0102dbff Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 19 Jun 2025 20:07:49 +0200 Subject: [PATCH 42/91] Fix layershell taking exclusive keyboard focus, preventing from clicking anything else --- rust/client/src/ui/windows/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/client/src/ui/windows/mod.rs b/rust/client/src/ui/windows/mod.rs index 0594d7b..71d6be1 100644 --- a/rust/client/src/ui/windows/mod.rs +++ b/rust/client/src/ui/windows/mod.rs @@ -387,7 +387,7 @@ pub fn hide_window(#[cfg(target_os = "linux")] wayland: bool, main_window_id: wi fn layer_shell_settings() -> iced_layershell::reexport::NewLayerShellSettings { iced_layershell::reexport::NewLayerShellSettings { layer: iced_layershell::reexport::Layer::Overlay, - keyboard_interactivity: iced_layershell::reexport::KeyboardInteractivity::Exclusive, + keyboard_interactivity: iced_layershell::reexport::KeyboardInteractivity::OnDemand, events_transparent: false, anchor: iced_layershell::reexport::Anchor::empty(), margin: Default::default(), From d1e61d4d19d2d1fc4b5bcaa7d1619ab040ceba76 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 22 Jun 2025 20:35:31 +0200 Subject: [PATCH 43/91] Massively simplify scenario runner --- Cargo.lock | 30 +- Cargo.toml | 6 +- dev_data/.gitignore | 1 + example_plugins/plugins/ui_grid/src/main.tsx | 4 +- .../plugins/ui_list/src/content-image.tsx | 2 +- .../ui_action_panel/action/default.json | 2 +- .../ui_action_panel/section/default.json | 2 +- .../ui_detail/content-code-block/default.json | 4 +- .../ui_detail/content-header/default.json | 4 +- .../content-horizontal-break/default.json | 4 +- .../ui_detail/content-image/default.json | 4 +- .../ui_detail/content-paragraph/default.json | 4 +- .../ui_detail/content-svg/default.json | 4 +- .../scenarios/ui_detail/content/default.json | 4 +- .../scenarios/ui_detail/main/default.json | 4 +- .../ui_detail/metadata-icon/default.json | 4 +- .../ui_detail/metadata-link/default.json | 4 +- .../ui_detail/metadata-separator/default.json | 4 +- .../ui_detail/metadata-tag-list/default.json | 4 +- .../ui_detail/metadata-value/default.json | 4 +- .../scenarios/ui_detail/metadata/default.json | 4 +- .../scenarios/ui_form/checkbox/default.json | 4 +- .../ui_form/date-picker/default.json | 3 - .../scenarios/ui_form/main/default.json | 4 +- .../ui_form/password-field/default.json | 4 +- .../scenarios/ui_form/select/default.json | 4 +- .../scenarios/ui_form/separator/default.json | 4 +- .../scenarios/ui_form/text-field/default.json | 4 +- .../ui_grid/content-code-block/default.json | 4 +- .../ui_grid/content-headers/default.json | 4 +- .../content-horizontal-break/default.json | 4 +- .../ui_grid/content-image/default.json | 4 +- .../ui_grid/content-paragraph/default.json | 4 +- .../ui_grid/content-svg/default.json | 4 +- .../scenarios/ui_grid/empty-view/default.json | 4 +- .../scenarios/ui_grid/focus/default.json | 4 +- .../scenarios/ui_grid/main/default.json | 4 +- .../ui_grid/more-columns/default.json | 4 +- .../scenarios/ui_grid/search-bar/default.json | 4 +- .../scenarios/ui_grid/section/default.json | 4 +- .../ui_icons_and_images/asset/default.json | 4 +- .../ui_icons_and_images/icon/default.json | 4 +- .../ui_icons_and_images/url/default.json | 4 +- .../ui_inline_code_block/main/default.json | 4 +- .../ui_inline_header/main/default.json | 4 +- .../main/default.json | 4 +- .../ui_inline_image/main/default.json | 4 +- .../ui_inline_main/main/default.json | 4 +- .../ui_inline_paragraph/main/default.json | 4 +- .../ui_inline_separator/main/default.json | 4 +- .../scenarios/ui_inline_svg/main/default.json | 4 +- .../main/default.json | 4 +- .../ui_inline_two_sections/main/default.json | 4 +- .../ui_list/content-code-block/default.json | 4 +- .../ui_list/content-headers/default.json | 4 +- .../content-horizontal-break/default.json | 4 +- .../ui_list/content-image/default.json | 4 +- .../ui_list/content-paragraph/default.json | 4 +- .../ui_list/content-svg/default.json | 4 +- .../scenarios/ui_list/content/default.json | 4 +- .../scenarios/ui_list/detail/default.json | 4 +- .../scenarios/ui_list/empty-view/default.json | 4 +- .../scenarios/ui_list/focus/default.json | 4 +- .../scenarios/ui_list/item/default.json | 4 +- .../scenarios/ui_list/main/default.json | 4 +- .../ui_list/metadata-icon/default.json | 4 +- .../ui_list/metadata-link/default.json | 4 +- .../ui_list/metadata-separator/default.json | 4 +- .../ui_list/metadata-tag-list/default.json | 4 +- .../ui_list/metadata-value/default.json | 4 +- .../scenarios/ui_list/metadata/default.json | 4 +- .../scenarios/ui_list/search-bar/default.json | 4 +- .../scenarios/ui_list/section/default.json | 4 +- js/scenario_runner_cli/src/main.ts | 156 +++------- package.json | 5 +- rust/cli/Cargo.toml | 1 - rust/cli/src/lib.rs | 24 +- rust/client/Cargo.toml | 5 +- rust/client/src/lib.rs | 23 +- rust/client/src/ui/mod.rs | 200 +++--------- rust/client/src/ui/scenario_runner.rs | 294 ++++++++++++++++++ rust/client/src/ui/server.rs | 4 +- rust/client/src/ui/windows/x11_focus.rs | 61 ++-- rust/common/src/lib.rs | 2 - rust/common/src/scenario_convert.rs | 16 - rust/common/src/scenario_model.rs | 70 ----- rust/plugin_runtime/Cargo.toml | 2 +- rust/plugin_runtime/src/lib.rs | 1 + rust/scenario_runner/Cargo.toml | 10 +- rust/scenario_runner/src/frontend_mock.rs | 233 -------------- rust/scenario_runner/src/lib.rs | 44 --- rust/scenario_runner/src/main.rs | 24 ++ rust/scenario_runner/src/model.rs | 9 - rust/server/Cargo.toml | 3 +- rust/server/src/lib.rs | 63 ---- rust/server/src/plugins/mod.rs | 6 + .../src/plugins/settings/global_shortcut.rs | 4 +- rust/server/src/plugins/settings/mod.rs | 5 + rust/utils/Cargo.toml | 2 + rust/utils/src/lib.rs | 2 + rust/utils/src/x11rb_async_atom_manager.rs | 84 +++++ rust/utils/src/x11rb_async_wm_class.rs | 109 +++++++ 102 files changed, 851 insertions(+), 921 deletions(-) create mode 100644 dev_data/.gitignore delete mode 100644 example_plugins/scenarios/ui_form/date-picker/default.json create mode 100644 rust/client/src/ui/scenario_runner.rs delete mode 100644 rust/common/src/scenario_convert.rs delete mode 100644 rust/common/src/scenario_model.rs delete mode 100644 rust/scenario_runner/src/frontend_mock.rs delete mode 100644 rust/scenario_runner/src/lib.rs create mode 100644 rust/scenario_runner/src/main.rs delete mode 100644 rust/scenario_runner/src/model.rs create mode 100644 rust/utils/src/x11rb_async_atom_manager.rs create mode 100644 rust/utils/src/x11rb_async_wm_class.rs diff --git a/Cargo.lock b/Cargo.lock index 439b9d0..62475b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4543,6 +4543,7 @@ dependencies = [ "anyhow", "arc-swap", "convert_case 0.6.0", + "futures", "gauntlet-common", "gauntlet-common-ui", "gauntlet-component-model", @@ -4560,7 +4561,7 @@ dependencies = [ "tokio", "tracing", "tray-icon", - "x11rb", + "x11rb-async", ] [[package]] @@ -4707,11 +4708,9 @@ name = "gauntlet-scenario-runner" version = "0.0.0" dependencies = [ "anyhow", - "gauntlet-common", - "gauntlet-utils", - "serde", - "serde_json", - "tokio", + "gauntlet-client", + "tracing", + "tracing-subscriber", ] [[package]] @@ -4726,7 +4725,6 @@ dependencies = [ "gauntlet-common", "gauntlet-common-plugin-runtime", "gauntlet-plugin-runtime", - "gauntlet-scenario-runner", "gauntlet-utils", "gauntlet-utils-macros", "git2", @@ -4766,6 +4764,8 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tonic", + "x11rb-async", + "x11rb-protocol", ] [[package]] @@ -13636,6 +13636,22 @@ dependencies = [ "x11rb-protocol", ] +[[package]] +name = "x11rb-async" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e550c5f8d07207e35f6b40288ac2debae694395926fb8554542963aeeaa95007" +dependencies = [ + "async-io", + "async-lock", + "blocking", + "event-listener 5.4.0", + "futures-lite", + "tracing", + "x11rb", + "x11rb-protocol", +] + [[package]] name = "x11rb-protocol" version = "0.13.1" diff --git a/Cargo.toml b/Cargo.toml index 9a8f161..a9b129f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ anyhow = { version = "1", features = ["backtrace"] } tracing = { version = "0.1" } tracing-subscriber = { version = "0.3", features = ["env-filter"] } tokio = { version = "1.42" } -tokio-util = "0.7" +tokio-util = { version = "0.7" } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0" } bincode = { version = "2.0.0-rc.3" } @@ -69,13 +69,15 @@ walkdir = { version = "2.4.0" } typed-path = { version = "0.10.0" } interprocess = { version = "2.2.2", features = ["tokio"] } toml = "0.8" +x11rb = { version = "0.13", features = ["extra-traits"] } +x11rb-async = { version = "0.13", features = ["extra-traits"] } +x11rb-protocol = { version = "0.13" } [dependencies] gauntlet-cli = { path = "rust/cli" } [features] release = ["gauntlet-cli/release"] -scenario_runner = ["gauntlet-cli/scenario_runner"] [profile.release-size] inherits = "release" diff --git a/dev_data/.gitignore b/dev_data/.gitignore new file mode 100644 index 0000000..a97afd7 --- /dev/null +++ b/dev_data/.gitignore @@ -0,0 +1 @@ +scenarios diff --git a/example_plugins/plugins/ui_grid/src/main.tsx b/example_plugins/plugins/ui_grid/src/main.tsx index 07ca131..82f64dd 100644 --- a/example_plugins/plugins/ui_grid/src/main.tsx +++ b/example_plugins/plugins/ui_grid/src/main.tsx @@ -28,7 +28,7 @@ const items = [ }, { title: "Dantooine", - image: "https://static.wikia.nocookie.net/starwars/images/a/a5/Dantooine_Resistance.jpg/revision/latest/scale-to-width-down/150?cb=20200120190043" + image: "https://static.wikia.nocookie.net/starwars/images/9/9b/Dantooine-RebuildingTheResistance.jpg/revision/latest/scale-to-width-down/150?cb=20200120190043" }, ] @@ -44,4 +44,4 @@ export default function MainExample(): ReactElement { ))} ) -} \ No newline at end of file +} diff --git a/example_plugins/plugins/ui_list/src/content-image.tsx b/example_plugins/plugins/ui_list/src/content-image.tsx index edc46c9..b0c335f 100644 --- a/example_plugins/plugins/ui_list/src/content-image.tsx +++ b/example_plugins/plugins/ui_list/src/content-image.tsx @@ -1,7 +1,7 @@ import { ReactElement } from "react"; import { List } from "@project-gauntlet/api/components"; -const imgUrl = "https://static.wikia.nocookie.net/starwars/images/e/e4/Ezaraa.png/revision/latest/scale-to-width-down/200?cb=20170511082800" +const imgUrl = "https://static.wikia.nocookie.net/starwars/images/9/9f/Ezaraa-TheScreamingCitadel1.png/revision/latest/scale-to-width-down/200?cb=20170511082800" export default function ContentImageExample(): ReactElement { return ( diff --git a/example_plugins/scenarios/ui_action_panel/action/default.json b/example_plugins/scenarios/ui_action_panel/action/default.json index 6281771..4eb459c 100644 --- a/example_plugins/scenarios/ui_action_panel/action/default.json +++ b/example_plugins/scenarios/ui_action_panel/action/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" + "type": "ViewWithActionPanel" } diff --git a/example_plugins/scenarios/ui_action_panel/section/default.json b/example_plugins/scenarios/ui_action_panel/section/default.json index 6281771..4eb459c 100644 --- a/example_plugins/scenarios/ui_action_panel/section/default.json +++ b/example_plugins/scenarios/ui_action_panel/section/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" + "type": "ViewWithActionPanel" } diff --git a/example_plugins/scenarios/ui_detail/content-code-block/default.json b/example_plugins/scenarios/ui_detail/content-code-block/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/content-code-block/default.json +++ b/example_plugins/scenarios/ui_detail/content-code-block/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/content-header/default.json b/example_plugins/scenarios/ui_detail/content-header/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/content-header/default.json +++ b/example_plugins/scenarios/ui_detail/content-header/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/content-horizontal-break/default.json b/example_plugins/scenarios/ui_detail/content-horizontal-break/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/content-horizontal-break/default.json +++ b/example_plugins/scenarios/ui_detail/content-horizontal-break/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/content-image/default.json b/example_plugins/scenarios/ui_detail/content-image/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/content-image/default.json +++ b/example_plugins/scenarios/ui_detail/content-image/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/content-paragraph/default.json b/example_plugins/scenarios/ui_detail/content-paragraph/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/content-paragraph/default.json +++ b/example_plugins/scenarios/ui_detail/content-paragraph/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/content-svg/default.json b/example_plugins/scenarios/ui_detail/content-svg/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/content-svg/default.json +++ b/example_plugins/scenarios/ui_detail/content-svg/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/content/default.json b/example_plugins/scenarios/ui_detail/content/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/content/default.json +++ b/example_plugins/scenarios/ui_detail/content/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/main/default.json b/example_plugins/scenarios/ui_detail/main/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/main/default.json +++ b/example_plugins/scenarios/ui_detail/main/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/metadata-icon/default.json b/example_plugins/scenarios/ui_detail/metadata-icon/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/metadata-icon/default.json +++ b/example_plugins/scenarios/ui_detail/metadata-icon/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/metadata-link/default.json b/example_plugins/scenarios/ui_detail/metadata-link/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/metadata-link/default.json +++ b/example_plugins/scenarios/ui_detail/metadata-link/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/metadata-separator/default.json b/example_plugins/scenarios/ui_detail/metadata-separator/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/metadata-separator/default.json +++ b/example_plugins/scenarios/ui_detail/metadata-separator/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/metadata-tag-list/default.json b/example_plugins/scenarios/ui_detail/metadata-tag-list/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/metadata-tag-list/default.json +++ b/example_plugins/scenarios/ui_detail/metadata-tag-list/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/metadata-value/default.json b/example_plugins/scenarios/ui_detail/metadata-value/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/metadata-value/default.json +++ b/example_plugins/scenarios/ui_detail/metadata-value/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_detail/metadata/default.json b/example_plugins/scenarios/ui_detail/metadata/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_detail/metadata/default.json +++ b/example_plugins/scenarios/ui_detail/metadata/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_form/checkbox/default.json b/example_plugins/scenarios/ui_form/checkbox/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_form/checkbox/default.json +++ b/example_plugins/scenarios/ui_form/checkbox/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_form/date-picker/default.json b/example_plugins/scenarios/ui_form/date-picker/default.json deleted file mode 100644 index 904b00b..0000000 --- a/example_plugins/scenarios/ui_form/date-picker/default.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "RequestViewRender" -} \ No newline at end of file diff --git a/example_plugins/scenarios/ui_form/main/default.json b/example_plugins/scenarios/ui_form/main/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_form/main/default.json +++ b/example_plugins/scenarios/ui_form/main/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_form/password-field/default.json b/example_plugins/scenarios/ui_form/password-field/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_form/password-field/default.json +++ b/example_plugins/scenarios/ui_form/password-field/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_form/select/default.json b/example_plugins/scenarios/ui_form/select/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_form/select/default.json +++ b/example_plugins/scenarios/ui_form/select/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_form/separator/default.json b/example_plugins/scenarios/ui_form/separator/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_form/separator/default.json +++ b/example_plugins/scenarios/ui_form/separator/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_form/text-field/default.json b/example_plugins/scenarios/ui_form/text-field/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_form/text-field/default.json +++ b/example_plugins/scenarios/ui_form/text-field/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/content-code-block/default.json b/example_plugins/scenarios/ui_grid/content-code-block/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/content-code-block/default.json +++ b/example_plugins/scenarios/ui_grid/content-code-block/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/content-headers/default.json b/example_plugins/scenarios/ui_grid/content-headers/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/content-headers/default.json +++ b/example_plugins/scenarios/ui_grid/content-headers/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/content-horizontal-break/default.json b/example_plugins/scenarios/ui_grid/content-horizontal-break/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/content-horizontal-break/default.json +++ b/example_plugins/scenarios/ui_grid/content-horizontal-break/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/content-image/default.json b/example_plugins/scenarios/ui_grid/content-image/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/content-image/default.json +++ b/example_plugins/scenarios/ui_grid/content-image/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/content-paragraph/default.json b/example_plugins/scenarios/ui_grid/content-paragraph/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/content-paragraph/default.json +++ b/example_plugins/scenarios/ui_grid/content-paragraph/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/content-svg/default.json b/example_plugins/scenarios/ui_grid/content-svg/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/content-svg/default.json +++ b/example_plugins/scenarios/ui_grid/content-svg/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/empty-view/default.json b/example_plugins/scenarios/ui_grid/empty-view/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/empty-view/default.json +++ b/example_plugins/scenarios/ui_grid/empty-view/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/focus/default.json b/example_plugins/scenarios/ui_grid/focus/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/focus/default.json +++ b/example_plugins/scenarios/ui_grid/focus/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/main/default.json b/example_plugins/scenarios/ui_grid/main/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/main/default.json +++ b/example_plugins/scenarios/ui_grid/main/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/more-columns/default.json b/example_plugins/scenarios/ui_grid/more-columns/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/more-columns/default.json +++ b/example_plugins/scenarios/ui_grid/more-columns/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/search-bar/default.json b/example_plugins/scenarios/ui_grid/search-bar/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/search-bar/default.json +++ b/example_plugins/scenarios/ui_grid/search-bar/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_grid/section/default.json b/example_plugins/scenarios/ui_grid/section/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_grid/section/default.json +++ b/example_plugins/scenarios/ui_grid/section/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_icons_and_images/asset/default.json b/example_plugins/scenarios/ui_icons_and_images/asset/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_icons_and_images/asset/default.json +++ b/example_plugins/scenarios/ui_icons_and_images/asset/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_icons_and_images/icon/default.json b/example_plugins/scenarios/ui_icons_and_images/icon/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_icons_and_images/icon/default.json +++ b/example_plugins/scenarios/ui_icons_and_images/icon/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_icons_and_images/url/default.json b/example_plugins/scenarios/ui_icons_and_images/url/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_icons_and_images/url/default.json +++ b/example_plugins/scenarios/ui_icons_and_images/url/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_inline_code_block/main/default.json b/example_plugins/scenarios/ui_inline_code_block/main/default.json index d880710..f327cc9 100644 --- a/example_plugins/scenarios/ui_inline_code_block/main/default.json +++ b/example_plugins/scenarios/ui_inline_code_block/main/default.json @@ -1,4 +1,4 @@ { - "type": "Search", + "type": "InlineView", "text": "example" -} \ No newline at end of file +} diff --git a/example_plugins/scenarios/ui_inline_header/main/default.json b/example_plugins/scenarios/ui_inline_header/main/default.json index d880710..f327cc9 100644 --- a/example_plugins/scenarios/ui_inline_header/main/default.json +++ b/example_plugins/scenarios/ui_inline_header/main/default.json @@ -1,4 +1,4 @@ { - "type": "Search", + "type": "InlineView", "text": "example" -} \ No newline at end of file +} diff --git a/example_plugins/scenarios/ui_inline_horizontal_break/main/default.json b/example_plugins/scenarios/ui_inline_horizontal_break/main/default.json index d880710..f327cc9 100644 --- a/example_plugins/scenarios/ui_inline_horizontal_break/main/default.json +++ b/example_plugins/scenarios/ui_inline_horizontal_break/main/default.json @@ -1,4 +1,4 @@ { - "type": "Search", + "type": "InlineView", "text": "example" -} \ No newline at end of file +} diff --git a/example_plugins/scenarios/ui_inline_image/main/default.json b/example_plugins/scenarios/ui_inline_image/main/default.json index d880710..f327cc9 100644 --- a/example_plugins/scenarios/ui_inline_image/main/default.json +++ b/example_plugins/scenarios/ui_inline_image/main/default.json @@ -1,4 +1,4 @@ { - "type": "Search", + "type": "InlineView", "text": "example" -} \ No newline at end of file +} diff --git a/example_plugins/scenarios/ui_inline_main/main/default.json b/example_plugins/scenarios/ui_inline_main/main/default.json index b9bad38..1cf31be 100644 --- a/example_plugins/scenarios/ui_inline_main/main/default.json +++ b/example_plugins/scenarios/ui_inline_main/main/default.json @@ -1,4 +1,4 @@ { - "type": "Search", + "type": "InlineView", "text": "1 + 2" -} \ No newline at end of file +} diff --git a/example_plugins/scenarios/ui_inline_paragraph/main/default.json b/example_plugins/scenarios/ui_inline_paragraph/main/default.json index d880710..f327cc9 100644 --- a/example_plugins/scenarios/ui_inline_paragraph/main/default.json +++ b/example_plugins/scenarios/ui_inline_paragraph/main/default.json @@ -1,4 +1,4 @@ { - "type": "Search", + "type": "InlineView", "text": "example" -} \ No newline at end of file +} diff --git a/example_plugins/scenarios/ui_inline_separator/main/default.json b/example_plugins/scenarios/ui_inline_separator/main/default.json index d880710..f327cc9 100644 --- a/example_plugins/scenarios/ui_inline_separator/main/default.json +++ b/example_plugins/scenarios/ui_inline_separator/main/default.json @@ -1,4 +1,4 @@ { - "type": "Search", + "type": "InlineView", "text": "example" -} \ No newline at end of file +} diff --git a/example_plugins/scenarios/ui_inline_svg/main/default.json b/example_plugins/scenarios/ui_inline_svg/main/default.json index d880710..f327cc9 100644 --- a/example_plugins/scenarios/ui_inline_svg/main/default.json +++ b/example_plugins/scenarios/ui_inline_svg/main/default.json @@ -1,4 +1,4 @@ { - "type": "Search", + "type": "InlineView", "text": "example" -} \ No newline at end of file +} diff --git a/example_plugins/scenarios/ui_inline_three_sections/main/default.json b/example_plugins/scenarios/ui_inline_three_sections/main/default.json index d880710..f327cc9 100644 --- a/example_plugins/scenarios/ui_inline_three_sections/main/default.json +++ b/example_plugins/scenarios/ui_inline_three_sections/main/default.json @@ -1,4 +1,4 @@ { - "type": "Search", + "type": "InlineView", "text": "example" -} \ No newline at end of file +} diff --git a/example_plugins/scenarios/ui_inline_two_sections/main/default.json b/example_plugins/scenarios/ui_inline_two_sections/main/default.json index d880710..f327cc9 100644 --- a/example_plugins/scenarios/ui_inline_two_sections/main/default.json +++ b/example_plugins/scenarios/ui_inline_two_sections/main/default.json @@ -1,4 +1,4 @@ { - "type": "Search", + "type": "InlineView", "text": "example" -} \ No newline at end of file +} diff --git a/example_plugins/scenarios/ui_list/content-code-block/default.json b/example_plugins/scenarios/ui_list/content-code-block/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/content-code-block/default.json +++ b/example_plugins/scenarios/ui_list/content-code-block/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/content-headers/default.json b/example_plugins/scenarios/ui_list/content-headers/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/content-headers/default.json +++ b/example_plugins/scenarios/ui_list/content-headers/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/content-horizontal-break/default.json b/example_plugins/scenarios/ui_list/content-horizontal-break/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/content-horizontal-break/default.json +++ b/example_plugins/scenarios/ui_list/content-horizontal-break/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/content-image/default.json b/example_plugins/scenarios/ui_list/content-image/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/content-image/default.json +++ b/example_plugins/scenarios/ui_list/content-image/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/content-paragraph/default.json b/example_plugins/scenarios/ui_list/content-paragraph/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/content-paragraph/default.json +++ b/example_plugins/scenarios/ui_list/content-paragraph/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/content-svg/default.json b/example_plugins/scenarios/ui_list/content-svg/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/content-svg/default.json +++ b/example_plugins/scenarios/ui_list/content-svg/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/content/default.json b/example_plugins/scenarios/ui_list/content/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/content/default.json +++ b/example_plugins/scenarios/ui_list/content/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/detail/default.json b/example_plugins/scenarios/ui_list/detail/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/detail/default.json +++ b/example_plugins/scenarios/ui_list/detail/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/empty-view/default.json b/example_plugins/scenarios/ui_list/empty-view/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/empty-view/default.json +++ b/example_plugins/scenarios/ui_list/empty-view/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/focus/default.json b/example_plugins/scenarios/ui_list/focus/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/focus/default.json +++ b/example_plugins/scenarios/ui_list/focus/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/item/default.json b/example_plugins/scenarios/ui_list/item/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/item/default.json +++ b/example_plugins/scenarios/ui_list/item/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/main/default.json b/example_plugins/scenarios/ui_list/main/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/main/default.json +++ b/example_plugins/scenarios/ui_list/main/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/metadata-icon/default.json b/example_plugins/scenarios/ui_list/metadata-icon/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/metadata-icon/default.json +++ b/example_plugins/scenarios/ui_list/metadata-icon/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/metadata-link/default.json b/example_plugins/scenarios/ui_list/metadata-link/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/metadata-link/default.json +++ b/example_plugins/scenarios/ui_list/metadata-link/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/metadata-separator/default.json b/example_plugins/scenarios/ui_list/metadata-separator/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/metadata-separator/default.json +++ b/example_plugins/scenarios/ui_list/metadata-separator/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/metadata-tag-list/default.json b/example_plugins/scenarios/ui_list/metadata-tag-list/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/metadata-tag-list/default.json +++ b/example_plugins/scenarios/ui_list/metadata-tag-list/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/metadata-value/default.json b/example_plugins/scenarios/ui_list/metadata-value/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/metadata-value/default.json +++ b/example_plugins/scenarios/ui_list/metadata-value/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/metadata/default.json b/example_plugins/scenarios/ui_list/metadata/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/metadata/default.json +++ b/example_plugins/scenarios/ui_list/metadata/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/search-bar/default.json b/example_plugins/scenarios/ui_list/search-bar/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/search-bar/default.json +++ b/example_plugins/scenarios/ui_list/search-bar/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/example_plugins/scenarios/ui_list/section/default.json b/example_plugins/scenarios/ui_list/section/default.json index 904b00b..e9bca5e 100644 --- a/example_plugins/scenarios/ui_list/section/default.json +++ b/example_plugins/scenarios/ui_list/section/default.json @@ -1,3 +1,3 @@ { - "type": "RequestViewRender" -} \ No newline at end of file + "type": "View" +} diff --git a/js/scenario_runner_cli/src/main.ts b/js/scenario_runner_cli/src/main.ts index 31cbfa1..dc53787 100644 --- a/js/scenario_runner_cli/src/main.ts +++ b/js/scenario_runner_cli/src/main.ts @@ -1,7 +1,7 @@ import { Command } from 'commander'; import { spawnSync } from "node:child_process"; import path from "node:path"; -import { existsSync, readdirSync, rmSync } from "node:fs"; +import { existsSync, rmSync } from "node:fs"; const program = new Command(); @@ -10,133 +10,63 @@ program .description('Gauntlet Scenario Runner Tool'); program.command('run-scenarios') - .argument('[plugin]') - .action(async (plugin) => { - await runScenarios(plugin) - }); - -program.command('run-screenshot-gen') .argument('[plugin]') .argument('[entrypoint]') .action(async (plugin, entrypoint) => { - await runScreenshotGen(plugin, entrypoint) + await runScenarios(plugin, entrypoint) }); await program.parseAsync(process.argv); -async function sleep(ms: number) { - return new Promise((r) => setTimeout(r, ms)); -} - -async function runScenarios(expectedPlugin: string | undefined) { +async function runScenarios( + expectedPlugin: string | undefined, + expectedEntrypoint: string | undefined +) { const projectRoot = path.resolve(process.cwd(), '..', '..'); - const scenarios = path.join(projectRoot, "example_plugins"); - const scenariosData = path.join(scenarios, "scenarios"); - const scenariosRun = path.join(scenarios, "run"); - - console.log("Building scenario plugins") + console.log("Building scenario plugins...") buildScenarioPlugins(projectRoot) - for (const pluginName of readdirSync(scenariosData)) { - if (expectedPlugin) { - if (pluginName != expectedPlugin) { - continue - } - } - - console.log("Starting runner") - - const backendProcess = spawnSync('cargo', ['run', '--features', 'scenario_runner'], { - stdio: "inherit", - cwd: projectRoot, - env: Object.assign(process.env, { - RUST_LOG: "gauntlet-server=INFO", - GAUNTLET_SCENARIO_RUNNER_TYPE: "scenario_runner", - GAUNTLET_SCENARIOS_DIR: scenarios, - GAUNTLET_SCENARIO_PLUGIN_NAME: pluginName, - XDG_DATA_HOME: path.join(scenariosRun, "data"), - XDG_CONFIG_HOME: path.join(scenariosRun, "config"), - XDG_CACHE_HOME: path.join(scenariosRun, "cache"), - XDG_STATE_HOME: path.join(scenariosRun, "state"), - }) - }) - - if (backendProcess.status !== 0) { - throw new Error(`Unable to run scenario runner, status: ${JSON.stringify(backendProcess)}`); - } - - if (existsSync(scenariosRun)) { - rmSync(scenariosRun, { recursive: true }) - } - - await sleep(1000) - } -} - -async function runScreenshotGen(expectedPlugin: string | undefined, expectedEntrypoint: string | undefined) { - const projectRoot = path.resolve(process.cwd(), '..', '..'); + console.log("Running scenario runner...") const scenarios = path.join(projectRoot, "example_plugins"); - const scenariosOut = path.join(scenarios, "out"); + const scenariosRun = path.join(scenarios, "run"); - const showActionPanelForPlugins = [ - "ui_action_panel" - ]; + const env: Record = { + RUST_BACKTRACE: "1", + RUST_LOG: "gauntlet_server=INFO,gauntlet_client=INFO,gauntlet_scenario_runner=INFO", + XDG_DATA_HOME: path.join(scenariosRun, "data"), + XDG_CONFIG_HOME: path.join(scenariosRun, "config"), + XDG_CACHE_HOME: path.join(scenariosRun, "cache"), + XDG_STATE_HOME: path.join(scenariosRun, "state"), + GAUNTLET_SCENARIOS_DIR: path.join(scenarios, "scenarios"), + GAUNTLET_SCENARIOS_PLUGINS_DIR: path.join(scenarios, "plugins"), + GAUNTLET_SCENARIOS_SCREENSHOTS_DIR: path.join(scenarios, "out_screenshot"), + }; - for (const plugin of readdirSync(scenariosOut)) { - if (expectedPlugin) { - if (plugin != expectedPlugin) { - continue - } - } - - const pluginDir = path.join(scenariosOut, plugin); - - for (const entrypoint of readdirSync(pluginDir)) { - if (expectedEntrypoint) { - if (entrypoint != expectedEntrypoint) { - continue - } - } - - const entrypointDir = path.join(pluginDir, entrypoint); - - for (const scenario of readdirSync(entrypointDir)) { - const scenarioFile = path.join(entrypointDir, scenario); - - console.log("Starting screenshot generating runner for scenario: " + scenarioFile) - - const scenarioName = path.parse(scenario).name; - const entrypointName = path.parse(entrypoint).name; - - let scenarioNameTitle = entrypointName - .split("-") - .filter(x => x.length > 0) - .map(x => (x.charAt(0).toUpperCase() + x.slice(1))) - .join(" "); - - const frontendReturn = spawnSync('cargo', ['run', '--features', 'scenario_runner'], { - stdio: "inherit", - cwd: projectRoot, - env: Object.assign(process.env, { - RUST_BACKTRACE: "1", - RUST_LOG: "gauntlet-client=INFO", - GAUNTLET_SCENARIO_RUNNER_TYPE: "screenshot_gen", - GAUNTLET_SCREENSHOT_GEN_IN: scenarioFile, - GAUNTLET_SCREENSHOT_GEN_OUT: path.join(scenarios, "out_screenshot", plugin, entrypoint, scenarioName + ".png"), - GAUNTLET_SCREENSHOT_GEN_NAME: scenarioNameTitle, - GAUNTLET_SCREENSHOT_SHOW_ACTION_PANEL: showActionPanelForPlugins.includes(plugin) ? "true" : "false", - }) - }); - - if (frontendReturn.status !== 0) { - throw new Error(`Unable to run frontend, status: ${JSON.stringify(frontendReturn)}`); - } - - console.log("Runner exited") - } - } + if (expectedPlugin) { + env.GAUNTLET_SCENARIOS_ONLY_PLUGIN = expectedPlugin; } + if (expectedEntrypoint) { + env.GAUNTLET_SCENARIOS_ONLY_ENTRYPOINT = expectedEntrypoint; + } + + const runReturn = spawnSync('cargo', ['run', '--package', 'gauntlet-scenario-runner', '--features', 'scenario_runner'], { + stdio: "inherit", + cwd: projectRoot, + env: Object.assign(process.env, env) + }); + + if (runReturn.status !== 0) { + throw new Error(`Unable to run scenario, status: ${JSON.stringify(runReturn)}`); + } + + console.log("Runner is done, cleaning up...") + + if (existsSync(scenariosRun)) { + rmSync(scenariosRun, { recursive: true }) + } + + console.log("Done") } function buildScenarioPlugins(projectRoot: string) { diff --git a/package.json b/package.json index a9e2bc9..912eb0f 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,11 @@ "private": true, "scripts": { "build-all": "npm run build --workspaces --if-present", - "build-scenarios": "npm run build --workspace example_plugins --if-present", + "build-scenarios": "npm run build --workspace example_plugins/plugins --if-present", "build-dev-plugin": "npm run build --workspace dev_plugin", "build": "npm run build --workspace js --workspace bundled_plugins --if-present", "run-component-model-gen": "npm run generate-json --workspace js/api_build", - "run-scenarios": "npm run run-scenarios --workspace js/scenario_runner_cli", - "run-screenshot-gen": "npm run run-screenshot-gen --workspace js/scenario_runner_cli" + "run-scenarios": "npm run run-scenarios --workspace js/scenario_runner_cli" }, "workspaces": [ "dev_plugin", diff --git a/rust/cli/Cargo.toml b/rust/cli/Cargo.toml index fd48cbc..b5a08e2 100644 --- a/rust/cli/Cargo.toml +++ b/rust/cli/Cargo.toml @@ -29,4 +29,3 @@ auto-launch = "0.5.0" [features] release = ["gauntlet-server/release"] -scenario_runner = ["gauntlet-server/scenario_runner", "gauntlet-client/scenario_runner"] diff --git a/rust/cli/src/lib.rs b/rust/cli/src/lib.rs index 4a8f0ac..259e827 100644 --- a/rust/cli/src/lib.rs +++ b/rust/cli/src/lib.rs @@ -94,23 +94,17 @@ pub fn init() { return; } - tracing::info!("Gauntlet Build Information:"); - for (name, value) in vergen_pretty_env!() { - if let Some(value) = value { - tracing::info!("{}: {}", name, value); + if is_server_running() { + open_window() + } else { + tracing::info!("Gauntlet Build Information:"); + for (name, value) in vergen_pretty_env!() { + if let Some(value) = value { + tracing::info!("{}: {}", name, value); + } } - } - #[cfg(feature = "scenario_runner")] - run_scenario_runner(); - - #[cfg(not(feature = "scenario_runner"))] - { - if is_server_running() { - open_window() - } else { - gauntlet_client::run_app(cli.minimized) - } + gauntlet_client::run_app(cli.minimized) } } Some(command) => { diff --git a/rust/client/Cargo.toml b/rust/client/Cargo.toml index 49fcb05..305186e 100644 --- a/rust/client/Cargo.toml +++ b/rust/client/Cargo.toml @@ -21,6 +21,7 @@ serde.workspace = true serde_json.workspace = true image.workspace = true once_cell.workspace = true +futures.workspace = true # other arc-swap = "1.7.1" @@ -30,7 +31,7 @@ tray-icon = { version = "0.19.2", default-features = false } [target.'cfg(target_os = "linux")'.dependencies] iced_layershell.workspace = true -x11rb = { version = "0.13", features = ["extra-traits"] } +x11rb-async.workspace = true [target.'cfg(target_os = "macos")'.dependencies] objc2-app-kit = { version = "0.2.2", features = ["NSWorkspace"] } @@ -41,4 +42,4 @@ anyhow.workspace = true convert_case = "0.6.0" [features] -scenario_runner = [] +scenario_runner = ["gauntlet-server/scenario_runner"] diff --git a/rust/client/src/lib.rs b/rust/client/src/lib.rs index 51449da..40bccd3 100644 --- a/rust/client/src/lib.rs +++ b/rust/client/src/lib.rs @@ -1,6 +1,27 @@ +use crate::ui::scenario_runner::ScenarioRunnerData; + mod model; mod ui; pub fn run_app(minimized: bool) { - ui::run(minimized); + ui::run(minimized, None); +} + +pub fn run_scenario( + scenarios_dir: String, + plugins_dir: String, + screenshots_dir: String, + only_plugin: Option, + only_entrypoint: Option, +) { + ui::run( + false, + Some(ScenarioRunnerData { + scenarios_dir, + plugins_dir, + screenshots_dir, + only_plugin, + only_entrypoint, + }), + ); } diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 71661e9..55bbcef 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -1,6 +1,4 @@ use std::collections::HashMap; -use std::fs; -use std::path::Path; use std::sync::Arc; use std::sync::Mutex; @@ -19,14 +17,12 @@ use gauntlet_common::model::UiTheme; use gauntlet_common::model::UiWidgetId; use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiRequestData; use gauntlet_common::rpc::server_grpc_api::ServerGrpcApiResponseData; -use gauntlet_common::scenario_convert::ui_render_location_from_scenario; -use gauntlet_common::scenario_model::ScenarioFrontendEvent; use gauntlet_common_ui::physical_key_model; use gauntlet_server::global_hotkey::GlobalHotKeyManager; use gauntlet_server::plugins::ApplicationManager; use gauntlet_server::plugins::settings::global_shortcut::GlobalShortcutAction; use gauntlet_server::plugins::settings::global_shortcut::GlobalShortcutPressedEvent; -use gauntlet_server::plugins::settings::global_shortcut::register_listener; +use gauntlet_server::plugins::settings::global_shortcut::register_global_shortcut_listener; use gauntlet_utils::channel::RequestError; use gauntlet_utils::channel::Responder; use iced::Event; @@ -57,7 +53,6 @@ use iced::widget::text::Shaping; use iced::widget::text_input; use iced::widget::text_input::focus; use iced::window; -use iced::window::Screenshot; use iced_fonts::BOOTSTRAP_FONT_BYTES; use crate::model::UiViewEvent; @@ -80,12 +75,16 @@ mod theme; mod widget; mod widget_container; +pub mod scenario_runner; mod server; mod windows; pub use theme::GauntletComplexTheme; use crate::ui::custom_widgets::loading_bar::LoadingBar; +use crate::ui::scenario_runner::ScenarioRunnerData; +use crate::ui::scenario_runner::ScenarioRunnerMsg; +use crate::ui::scenario_runner::handle_scenario_runner_msg; use crate::ui::scroll_handle::ScrollHandle; use crate::ui::server::handle_server_message; use crate::ui::state::ErrorViewData; @@ -208,13 +207,6 @@ pub enum AppMsg { plugin_id: PluginId, entrypoint_id: EntrypointId, }, - Screenshot { - save_path: String, - }, - ScreenshotDone { - save_path: String, - screenshot: Screenshot, - }, ShowBackendError(RequestError), ClosePluginView, OpenPluginView(PluginId, EntrypointId), @@ -286,9 +278,10 @@ pub enum AppMsg { SetHudDisplay { display: String, }, + HandleScenario(ScenarioRunnerMsg), } -pub fn run(minimized: bool) { +pub fn run(minimized: bool, scenario_runner_data: Option) { #[cfg(target_os = "linux")] let result = { let wayland = std::env::var("WAYLAND_DISPLAY") @@ -296,24 +289,29 @@ pub fn run(minimized: bool) { .is_ok(); if wayland { - run_wayland(minimized) + run_wayland(minimized, scenario_runner_data) } else { - run_non_wayland(minimized) + run_non_wayland(minimized, scenario_runner_data) } }; #[cfg(not(target_os = "linux"))] - let result = run_non_wayland(minimized); + let result = run_non_wayland( + minimized, + #[cfg(feature = "scenario_runner")] + scenario_runner_data, + ); result.expect("Unable to start application") } -fn run_non_wayland(minimized: bool) -> anyhow::Result<()> { +fn run_non_wayland(minimized: bool, scenario_runner_data: Option) -> anyhow::Result<()> { let boot = move || { new( #[cfg(target_os = "linux")] false, minimized, + scenario_runner_data.clone(), ) }; @@ -336,8 +334,8 @@ fn run_non_wayland(minimized: bool) -> anyhow::Result<()> { } #[cfg(target_os = "linux")] -fn run_wayland(minimized: bool) -> anyhow::Result<()> { - let boot = move || new(true, minimized); +fn run_wayland(minimized: bool, scenario_runner_data: Option) -> anyhow::Result<()> { + let boot = move || new(true, minimized, scenario_runner_data.clone()); iced_layershell::build_pattern::daemon(boot, "gauntlet", update, view) .layer_settings(iced_layershell::settings::LayerShellSettings { @@ -354,7 +352,11 @@ fn run_wayland(minimized: bool) -> anyhow::Result<()> { Ok(()) } -fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, Task) { +fn new( + #[cfg(target_os = "linux")] wayland: bool, + minimized: bool, + #[allow(unused)] scenario_runner_data: Option, +) -> (AppModel, Task) { let (application_manager, global_hotkey_manager, setup_data, setup_task) = server::setup(); let theme = GauntletComplexTheme::new(setup_data.theme); @@ -371,14 +373,14 @@ fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, tasks.push(open_task.map(AppMsg::WindowAction)); tasks.push(setup_task); - let mut client_context = ClientContext::new(); - - let global_state = if cfg!(feature = "scenario_runner") { - scenario_runner_global_state(&mut tasks, &mut client_context) - } else { - GlobalState::new(text_input::Id::unique()) - }; + #[cfg(feature = "scenario_runner")] + tasks.push(scenario_runner::run_scenario( + scenario_runner_data.unwrap(), + application_manager.get_scenarios_theme(), + )); + let client_context = ClientContext::new(); + let global_state = GlobalState::new(text_input::Id::unique()); let window = WindowState::new( main_window_id, minimized, @@ -413,108 +415,6 @@ fn new(#[cfg(target_os = "linux")] wayland: bool, minimized: bool) -> (AppModel, ) } -fn scenario_runner_global_state(tasks: &mut Vec>, client_context: &mut ClientContext) -> GlobalState { - let gen_in = std::env::var("GAUNTLET_SCREENSHOT_GEN_IN").expect("Unable to read GAUNTLET_SCREENSHOT_GEN_IN"); - - println!("Reading scenario file at: {}", gen_in); - - let gen_in = fs::read_to_string(gen_in).expect("Unable to read file at GAUNTLET_SCREENSHOT_GEN_IN"); - - let gen_out = std::env::var("GAUNTLET_SCREENSHOT_GEN_OUT").expect("Unable to read GAUNTLET_SCREENSHOT_GEN_OUT"); - - let gen_name = std::env::var("GAUNTLET_SCREENSHOT_GEN_NAME").expect("Unable to read GAUNTLET_SCREENSHOT_GEN_NAME"); - - let show_action_panel: bool = std::env::var("GAUNTLET_SCREENSHOT_SHOW_ACTION_PANEL") - .expect("Unable to read GAUNTLET_SCREENSHOT_SHOW_ACTION_PANEL") - .parse() - .expect("Unable to parse GAUNTLET_SCREENSHOT_SHOW_ACTION_PANEL"); - - let event: ScenarioFrontendEvent = - serde_json::from_str(&gen_in).expect("GAUNTLET_SCREENSHOT_GEN_IN is not valid json"); - - tasks.push(Task::perform( - async { - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; - }, - move |_| { - AppMsg::Screenshot { - save_path: gen_out.clone(), - } - }, - )); - - match event { - ScenarioFrontendEvent::ReplaceView { - entrypoint_id, - render_location, - top_level_view, - container, - data: images, - } => { - let plugin_id = PluginId::from_string("__SCREENSHOT_GEN___"); - let entrypoint_id = EntrypointId::from_string(entrypoint_id); - let plugin_name = "Screenshot Plugin".to_string(); - let entrypoint_name = gen_name; - - let render_location = ui_render_location_from_scenario(render_location); - - let _ = client_context.render_ui( - render_location, - Arc::new(container), - images, - &plugin_id, - &plugin_name, - &entrypoint_id, - &entrypoint_name, - ); - - if show_action_panel { - tasks.push(Task::done(AppMsg::ToggleActionPanel { keyboard: false })) - } - - match render_location { - UiRenderLocation::InlineView => GlobalState::new(text_input::Id::unique()), - UiRenderLocation::View => { - GlobalState::new_plugin( - PluginViewData { - top_level_view, - plugin_id, - entrypoint_id, - action_shortcuts: Default::default(), - }, - true, - ) - } - } - } - ScenarioFrontendEvent::ShowPreferenceRequiredView { - entrypoint_id, - plugin_preferences_required, - entrypoint_preferences_required, - } => { - let error_view = ErrorViewData::PreferenceRequired { - plugin_id: PluginId::from_string("__SCREENSHOT_GEN___"), - entrypoint_id: EntrypointId::from_string(entrypoint_id), - plugin_preferences_required, - entrypoint_preferences_required, - }; - - GlobalState::new_error(error_view) - } - ScenarioFrontendEvent::ShowPluginErrorView { - entrypoint_id, - render_location: _, - } => { - let error_view = ErrorViewData::PluginError { - plugin_id: PluginId::from_string("__SCREENSHOT_GEN___"), - entrypoint_id: EntrypointId::from_string(entrypoint_id), - }; - - GlobalState::new_error(error_view) - } - } -} - fn title(state: &AppModel, window: window::Id) -> String { if window == state.window.main_window_id { "Gauntlet".to_owned() @@ -977,40 +877,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } - AppMsg::Screenshot { save_path } => { - println!("Creating screenshot at: {}", save_path); - - fs::create_dir_all(Path::new(&save_path).parent().expect("no parent?")) - .expect("unable to create scenario out directories"); - - window::screenshot(state.window.main_window_id).map(move |screenshot| { - AppMsg::ScreenshotDone { - save_path: save_path.clone(), - screenshot, - } - }) - } - AppMsg::ScreenshotDone { save_path, screenshot } => { - println!("Saving screenshot at: {}", save_path); - - let save_dir = Path::new(&save_path); - - let save_parent_dir = save_dir.parent().expect("save_path has no parent"); - - fs::create_dir_all(save_parent_dir).expect("unable to create save_parent_dir"); - - image::save_buffer_with_format( - &save_path, - &screenshot.bytes, - screenshot.size.width, - screenshot.size.height, - image::ColorType::Rgba8, - image::ImageFormat::Png, - ) - .expect("Unable to save screenshot"); - - std::process::exit(0); - } AppMsg::ToggleActionPanel { keyboard } => { match &mut state.global_state { GlobalState::MainView { @@ -1362,6 +1228,10 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } + AppMsg::HandleScenario(msg) => { + handle_scenario_runner_msg(msg, state.application_manager.clone(), state.window.main_window_id) + .map(AppMsg::HandleScenario) + } } } @@ -1930,7 +1800,7 @@ fn subscription(#[allow(unused)] state: &AppModel) -> Subscription { let mut subscriptions = vec![ Subscription::run(|| { stream::channel(10, async move |sender| { - register_listener(sender.clone()); + register_global_shortcut_listener(sender.clone()); std::future::pending::<()>().await; diff --git a/rust/client/src/ui/scenario_runner.rs b/rust/client/src/ui/scenario_runner.rs new file mode 100644 index 0000000..396914c --- /dev/null +++ b/rust/client/src/ui/scenario_runner.rs @@ -0,0 +1,294 @@ +#![allow(unused)] + +use std::fs; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + +use anyhow::Context; +use anyhow::anyhow; +use gauntlet_common::model::EntrypointId; +use gauntlet_common::model::PluginId; +use gauntlet_common::model::UiTheme; +use gauntlet_server::plugins::ApplicationManager; +use iced::Task; +use iced::advanced::graphics::futures::MaybeSend; +use iced::window; +use iced::window::Screenshot; +use serde::Deserialize; +use serde::Serialize; +use tokio::time::Duration; +use tokio::time::sleep; + +use crate::ui::AppMsg; +use crate::ui::windows::WindowActionMsg; + +#[derive(Clone, Debug)] +pub struct ScenarioRunnerData { + pub scenarios_dir: String, + pub plugins_dir: String, + pub screenshots_dir: String, + pub only_plugin: Option, + pub only_entrypoint: Option, +} + +#[derive(Clone, Debug)] +pub enum ScenarioRunnerMsg { + AddScenarioPlugin { plugin_path: String }, + Screenshot { save_path: PathBuf }, + ScreenshotDone { save_path: PathBuf, screenshot: Screenshot }, + RemoveScenarioPlugin { plugin_id: PluginId }, + Shutdown, +} + +pub fn handle_scenario_runner_msg( + msg: ScenarioRunnerMsg, + application_manager: Arc, + main_window_id: window::Id, +) -> Task { + match msg { + ScenarioRunnerMsg::AddScenarioPlugin { plugin_path } => { + application_manager + .save_local_plugin(&plugin_path) + .expect("Unable to save scenario plugin"); + + Task::none() + } + ScenarioRunnerMsg::Screenshot { save_path } => { + window::screenshot(main_window_id).map(move |screenshot| { + ScenarioRunnerMsg::ScreenshotDone { + save_path: save_path.clone(), + screenshot, + } + }) + } + ScenarioRunnerMsg::ScreenshotDone { save_path, screenshot } => { + println!("Saving screenshot at: {:?}", save_path); + + fs::create_dir_all(Path::new(&save_path).parent().unwrap()) + .expect("unable to create scenario out directories"); + + image::save_buffer_with_format( + &save_path, + &screenshot.bytes, + screenshot.size.width, + screenshot.size.height, + image::ColorType::Rgba8, + image::ImageFormat::Png, + ) + .expect("Unable to save screenshot"); + + Task::none() + } + ScenarioRunnerMsg::RemoveScenarioPlugin { plugin_id } => { + application_manager + .remove_plugin(plugin_id) + .expect("Unable to remove plugin"); + + Task::none() + } + ScenarioRunnerMsg::Shutdown => { + tracing::info!("Shutting down scenario"); + + iced::exit() + } + } +} + +pub fn run_scenario(data: ScenarioRunnerData, theme: UiTheme) -> Task { + tracing::info!("scenario inputs: {:?}", &data); + + let scenario_data = collect_scenario_data( + PathBuf::from(data.scenarios_dir), + PathBuf::from(data.plugins_dir), + PathBuf::from(data.screenshots_dir), + data.only_plugin, + data.only_entrypoint, + ) + .expect("Unable to collect data"); + + let mut chain = Task::none().chain(Task::done(AppMsg::SetTheme { theme })); + + let shutdown = AppMsg::HandleScenario(ScenarioRunnerMsg::Shutdown); + + for work_item in scenario_data { + let plugin_path = work_item.plugin_path.clone(); + let plugin_id = PluginId::from_string(format!("file://{}", &plugin_path.clone())); + + let add_plugin = AppMsg::HandleScenario(ScenarioRunnerMsg::AddScenarioPlugin { + plugin_path: plugin_path.clone(), + }); + let remove_plugin = AppMsg::HandleScenario(ScenarioRunnerMsg::RemoveScenarioPlugin { + plugin_id: plugin_id.clone(), + }); + + chain = chain.chain(Task::done(add_plugin)).chain(wait_for(2500)); + + for work_sub_item in work_item.entrypoints { + let entrypoint_id = EntrypointId::from_string(work_sub_item.entrypoint_id); + let save_path = work_sub_item.screenshot_out_path; + + let show_window = AppMsg::WindowAction(WindowActionMsg::ShowWindow); + let close_window = AppMsg::WindowAction(WindowActionMsg::HideWindow); + let run_entrypoint = AppMsg::RunEntrypoint { + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + }; + let do_screenshot = AppMsg::HandleScenario(ScenarioRunnerMsg::Screenshot { save_path }); + let open_action_panel = AppMsg::ToggleActionPanel { keyboard: false }; + + let log = Task::future(async move { + println!("Running scenario for entrypoint: {}", entrypoint_id); + }); + + chain = chain + .chain(Task::done(show_window)) + .chain(log.discard()) + .chain(wait_for(100)); + + match work_sub_item.item_type { + ScenarioWorkSubType::InlineView { text } => { + chain = chain.chain(Task::done(AppMsg::PromptChanged(text))) + } + ScenarioWorkSubType::View => { + chain = chain.chain(Task::done(run_entrypoint)); + } + ScenarioWorkSubType::ViewWithActionPanel => { + chain = chain + .chain(Task::done(run_entrypoint)) + .chain(wait_for(500)) + .chain(Task::done(open_action_panel)); + } + }; + + chain = chain + .chain(wait_for(1500)) + .chain(Task::done(do_screenshot)) + .chain(Task::done(close_window)) + .chain(wait_for(500)) + } + + chain = chain.chain(Task::done(remove_plugin)).chain(wait_for(1000)); + } + + chain = chain.chain(Task::done(shutdown)); + + chain +} + +struct ScenarioWorkItem { + plugin_path: String, + entrypoints: Vec, +} + +struct ScenarioWorkSubItem { + entrypoint_id: String, + screenshot_out_path: PathBuf, + item_type: ScenarioWorkSubType, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(tag = "type")] +pub enum ScenarioWorkSubType { + InlineView { text: String }, + View, + ViewWithActionPanel, +} + +fn collect_scenario_data( + scenarios_dir: PathBuf, + plugins_dir: PathBuf, + screenshot_out: PathBuf, + only_plugin: Option, + only_entrypoint: Option, +) -> anyhow::Result> { + let mut results = vec![]; + + for scenario_dir in fs::read_dir(&scenarios_dir)? { + let scenario_dir = scenario_dir?; + + let scenario_name = scenario_dir.file_name().to_str().context("invalid UTF-8")?.to_string(); + + if let Some(only_plugin) = &only_plugin { + if only_plugin != &scenario_name { + continue; + } + } + + tracing::info!("scenario: {:?}", &scenario_name); + + let plugin_path = plugins_dir.join(&scenario_name); + let screenshot_out_dir = screenshot_out.join(&scenario_name); + + let mut entrypoints = vec![]; + + for entrypoint in fs::read_dir(&scenario_dir.path())? { + let entrypoint = entrypoint?; + if !entrypoint.metadata()?.is_dir() { + return Err(anyhow!("unexpected file {:?}", &entrypoint)); + } + + let entrypoint_id = entrypoint.file_name().to_str().context("invalid UTF-8")?.to_string(); + + if let Some(only_entrypoint) = &only_entrypoint { + if only_entrypoint != &entrypoint_id { + continue; + } + } + + tracing::info!("entrypoint: {}", &entrypoint_id); + + let entrypoint_dir = entrypoint.path(); + let screenshot_out_dir = screenshot_out_dir.join(&entrypoint_id); + + for scenario_item in fs::read_dir(&entrypoint_dir)? { + let scenario_item = scenario_item?; + if !scenario_item.metadata()?.is_file() { + return Err(anyhow!("unexpected file {:?}", &scenario_item)); + } + + let scenario_item_name = scenario_item + .path() + .file_stem() + .unwrap() + .to_str() + .context("invalid UTF-8")? + .to_string(); + + let scenario_item_path = scenario_item.path(); + + if &scenario_item_name != "default" { + todo!(); + } + + tracing::info!("scenario item: {:?}", &scenario_item_name); + + let screenshot_out_path = screenshot_out_dir.join(&format!("{}.png", scenario_item_name)); + + let scenario_data = fs::read(&scenario_item_path)?; + + let item_type = serde_json::from_slice(&scenario_data)?; + + entrypoints.push(ScenarioWorkSubItem { + entrypoint_id: entrypoint_id.clone(), + screenshot_out_path, + item_type, + }) + } + } + + results.push(ScenarioWorkItem { + plugin_path: plugin_path.to_str().context("invalid UTF-8")?.to_string(), + entrypoints, + }) + } + + Ok(results) +} + +fn wait_for(millis: u64) -> Task +where + O: MaybeSend + 'static, +{ + Task::future(async move { sleep(Duration::from_millis(millis)).await }).discard() +} diff --git a/rust/client/src/ui/server.rs b/rust/client/src/ui/server.rs index 7cbeaab..355dfbb 100644 --- a/rust/client/src/ui/server.rs +++ b/rust/client/src/ui/server.rs @@ -35,14 +35,14 @@ pub fn setup() -> (Arc, GlobalHotKeyManager, UiSetupData, Ta let server_grpc_receiver = Arc::new(TokioRwLock::new(server_grpc_receiver)); let application_manager = Arc::new(application_manager); - tokio::spawn(async move { run_grpc_server(grpc_api).await }); - let setup_data = application_manager .setup(&global_hotkey_manager) .expect("Unable to setup"); let mut tasks = vec![]; + tasks.push(Task::future(async move { run_grpc_server(grpc_api).await }).discard()); + tasks.push(Task::stream(stream::channel(10, |mut sender| { async move { let mut frontend_receiver = frontend_receiver.write().await; diff --git a/rust/client/src/ui/windows/x11_focus.rs b/rust/client/src/ui/windows/x11_focus.rs index 91a1572..0ef6428 100644 --- a/rust/client/src/ui/windows/x11_focus.rs +++ b/rust/client/src/ui/windows/x11_focus.rs @@ -1,19 +1,19 @@ use std::convert::Infallible; use anyhow::anyhow; +use gauntlet_utils::x11rb_async_wm_class::WmClass; use iced::Subscription; use iced::futures::SinkExt; use iced::futures::channel::mpsc::Sender; use iced::stream; use tokio::runtime::Handle; -use x11rb::connection::Connection; -use x11rb::properties::WmClass; -use x11rb::protocol::xproto::AtomEnum; -use x11rb::protocol::xproto::ChangeWindowAttributesAux; -use x11rb::protocol::xproto::ConnectionExt; -use x11rb::protocol::xproto::EventMask; -use x11rb::protocol::xproto::Window; -use x11rb::rust_connection::RustConnection; +use x11rb_async::connection::Connection; +use x11rb_async::protocol::xproto::AtomEnum; +use x11rb_async::protocol::xproto::ChangeWindowAttributesAux; +use x11rb_async::protocol::xproto::ConnectionExt; +use x11rb_async::protocol::xproto::EventMask; +use x11rb_async::protocol::xproto::Window; +use x11rb_async::rust_connection::RustConnection; use crate::ui::AppMsg; use crate::ui::windows::WindowActionMsg; @@ -23,32 +23,36 @@ pub fn x11_linux_focus_change_subscription() -> Subscription { stream::channel(100, async move |sender| { let handle = Handle::current(); - let err = tokio::task::spawn_blocking(|| listen_on_x11_active_window_change(sender, handle)).await; + let Err(err) = listen_on_x11_active_window_change(sender, handle).await; - if let Err(err) = err { - tracing::error!("error occurred when listening on x11 events: {:?}", err); - } + tracing::error!("error occurred when listening on x11 events: {:?}", err); }) }) } -fn listen_on_x11_active_window_change(sender: Sender, handle: Handle) -> anyhow::Result { - let (conn, screen_num) = RustConnection::connect(None)?; +async fn listen_on_x11_active_window_change(sender: Sender, handle: Handle) -> anyhow::Result { + let (conn, screen_num, drive) = RustConnection::connect(None).await?; + + tokio::spawn(async move { + let Err(e) = drive.await; + tracing::error!("Error while driving the x11 connection: {}", e); + }); + let screen = &conn.setup().roots[screen_num]; - let atoms = atoms::Atoms::new(&conn)?.reply()?; + let atoms = atoms::Atoms::new(&conn).await?.reply().await?; let aux = ChangeWindowAttributesAux::new().event_mask(EventMask::PROPERTY_CHANGE); - conn.change_window_attributes(screen.root, &aux)?.check()?; + conn.change_window_attributes(screen.root, &aux).await?.check().await?; loop { - if let x11rb::protocol::Event::PropertyNotify(event) = conn.wait_for_event()? { + if let x11rb_async::protocol::Event::PropertyNotify(event) = conn.wait_for_event().await? { if event.atom == atoms._NET_ACTIVE_WINDOW { - let Ok(window) = fetch_window_id(&conn, screen.root, &atoms) else { + let Ok(window) = fetch_window_id(&conn, screen.root, &atoms).await else { continue; }; - let wm_name = fetch_app_wm_name(&conn, window).ok(); + let wm_name = fetch_app_wm_name(&conn, window).await.ok(); let mut sender = sender.clone(); handle.spawn(async move { @@ -64,10 +68,12 @@ fn listen_on_x11_active_window_change(sender: Sender, handle: Handle) -> } } -fn fetch_window_id(conn: &impl Connection, root: Window, atoms: &atoms::Atoms) -> anyhow::Result { +async fn fetch_window_id(conn: &impl Connection, root: Window, atoms: &atoms::Atoms) -> anyhow::Result { let window = conn - .get_property(false, root, atoms._NET_ACTIVE_WINDOW, AtomEnum::WINDOW, 0, 1)? - .reply()? + .get_property(false, root, atoms._NET_ACTIVE_WINDOW, AtomEnum::WINDOW, 0, 1) + .await? + .reply() + .await? .value32() .ok_or(anyhow!("_NET_ACTIVE_WINDOW has incorrect format"))? .next() @@ -76,16 +82,19 @@ fn fetch_window_id(conn: &impl Connection, root: Window, atoms: &atoms::Atoms) - Ok(window) } -fn fetch_app_wm_name(conn: &impl Connection, window_id: Window) -> anyhow::Result { - let wm_class = WmClass::get(conn, window_id)?; - let wm_class = wm_class.reply()?.ok_or(anyhow!("no WM_CLASS prop on the window"))?; +async fn fetch_app_wm_name(conn: &impl Connection, window_id: Window) -> anyhow::Result { + let wm_class = WmClass::get(conn, window_id).await?; + let wm_class = wm_class + .reply() + .await? + .ok_or(anyhow!("no WM_CLASS prop on the window"))?; let class = std::str::from_utf8(wm_class.class())?; Ok(class.to_string()) } mod atoms { - x11rb::atom_manager! { + gauntlet_utils::atom_manager! { pub Atoms: AtomsCookie { _NET_ACTIVE_WINDOW, diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs index ea8b1bb..7bdf57d 100644 --- a/rust/common/src/lib.rs +++ b/rust/common/src/lib.rs @@ -6,8 +6,6 @@ pub mod detached_process; pub mod dirs; pub mod model; pub mod rpc; -pub mod scenario_convert; -pub mod scenario_model; pub const SETTINGS_ENV: &'static str = "__GAUNTLET_INTERNAL_SETTINGS__"; diff --git a/rust/common/src/scenario_convert.rs b/rust/common/src/scenario_convert.rs deleted file mode 100644 index bcbf0d1..0000000 --- a/rust/common/src/scenario_convert.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::model::UiRenderLocation; -use crate::scenario_model::ScenarioUiRenderLocation; - -pub fn ui_render_location_to_scenario(render_location: UiRenderLocation) -> ScenarioUiRenderLocation { - match render_location { - UiRenderLocation::InlineView => ScenarioUiRenderLocation::InlineView, - UiRenderLocation::View => ScenarioUiRenderLocation::View, - } -} - -pub fn ui_render_location_from_scenario(render_location: ScenarioUiRenderLocation) -> UiRenderLocation { - match render_location { - ScenarioUiRenderLocation::InlineView => UiRenderLocation::InlineView, - ScenarioUiRenderLocation::View => UiRenderLocation::View, - } -} diff --git a/rust/common/src/scenario_model.rs b/rust/common/src/scenario_model.rs deleted file mode 100644 index 0bd881e..0000000 --- a/rust/common/src/scenario_model.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::collections::HashMap; - -use serde::Deserialize; -use serde::Serialize; - -use crate::model::RootWidget; -use crate::model::UiWidgetId; - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)] -pub enum ScenarioUiRenderLocation { - InlineView, - View, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(tag = "type")] -pub enum ScenarioFrontendEvent { - ReplaceView { - entrypoint_id: String, - render_location: ScenarioUiRenderLocation, - top_level_view: bool, - container: RootWidget, - #[serde(with = "base64")] - data: HashMap>, - }, - ShowPreferenceRequiredView { - entrypoint_id: String, - plugin_preferences_required: bool, - entrypoint_preferences_required: bool, - }, - ShowPluginErrorView { - entrypoint_id: String, - render_location: ScenarioUiRenderLocation, - }, -} - -mod base64 { - use std::collections::HashMap; - use std::str::FromStr; - - use base64::Engine; - use base64::engine::general_purpose::STANDARD; - use serde::Deserialize; - use serde::Deserializer; - use serde::Serialize; - use serde::Serializer; - - use crate::model::UiWidgetId; - - pub fn serialize(v: &HashMap>, s: S) -> Result { - let map = v - .iter() - .map(|(key, value)| (key.to_string(), STANDARD.encode(value))) - .collect(); - - HashMap::::serialize(&map, s) - } - - pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result>, D::Error> { - HashMap::::deserialize(d)? - .into_iter() - .map(|(key, value)| { - STANDARD - .decode(value.as_bytes()) - .map_err(|e| serde::de::Error::custom(e)) - .map(|vec| (UiWidgetId::from_str(&key).expect("should not fail"), vec)) - }) - .collect() - } -} diff --git a/rust/plugin_runtime/Cargo.toml b/rust/plugin_runtime/Cargo.toml index 7b429b6..bd573b8 100644 --- a/rust/plugin_runtime/Cargo.toml +++ b/rust/plugin_runtime/Cargo.toml @@ -27,6 +27,7 @@ bytes.workspace = true walkdir.workspace = true typed-path.workspace = true interprocess.workspace = true +x11rb.workspace = true # deno crates deno_core = { version = "=0.347.0" } # https://github.com/denoland/deno/blob/v2.3.3 @@ -50,7 +51,6 @@ wayland-protocols-wlr = { version = "0.3.5", features = ["client"] } cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols.git" } wayland-client = "0.31.7" smithay-client-toolkit = "0.19.2" -x11rb = { version = "0.13", features = ["extra-traits"] } encoding = "0.2" [target.'cfg(target_os = "macos")'.dependencies] diff --git a/rust/plugin_runtime/src/lib.rs b/rust/plugin_runtime/src/lib.rs index a228c29..216daa4 100644 --- a/rust/plugin_runtime/src/lib.rs +++ b/rust/plugin_runtime/src/lib.rs @@ -45,6 +45,7 @@ use crate::deno::start_js_runtime; pub fn run_plugin_runtime(socket_name: String) { #[cfg(target_os = "linux")] + #[cfg(not(feature = "scenario_runner"))] unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL); } diff --git a/rust/scenario_runner/Cargo.toml b/rust/scenario_runner/Cargo.toml index 837dca2..3cddc6c 100644 --- a/rust/scenario_runner/Cargo.toml +++ b/rust/scenario_runner/Cargo.toml @@ -4,12 +4,12 @@ edition.workspace = true [dependencies] # workspaces -gauntlet-common.workspace = true -gauntlet-utils.workspace = true +gauntlet-client.workspace = true # shared -tokio.workspace = true anyhow.workspace = true -serde.workspace = true -serde_json.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +[features] +scenario_runner = ["gauntlet-client/scenario_runner"] diff --git a/rust/scenario_runner/src/frontend_mock.rs b/rust/scenario_runner/src/frontend_mock.rs deleted file mode 100644 index 991406e..0000000 --- a/rust/scenario_runner/src/frontend_mock.rs +++ /dev/null @@ -1,233 +0,0 @@ -use std::fs; -use std::path::Path; - -use gauntlet_common::model::EntrypointId; -use gauntlet_common::model::PluginId; -use gauntlet_common::rpc::backend_api::BackendForFrontendApi; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiProxy; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiRequestData; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiResponseData; -use gauntlet_common::rpc::backend_api::GrpcBackendApi; -use gauntlet_common::rpc::backend_server::wait_for_backend_server; -use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; -use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; -use gauntlet_common::scenario_convert::ui_render_location_to_scenario; -use gauntlet_common::scenario_model::ScenarioFrontendEvent; -use gauntlet_utils::channel::RequestReceiver; -use gauntlet_utils::channel::RequestSender; - -use crate::model::ScenarioBackendEvent; - -pub async fn start_scenario_runner_frontend( - request_receiver: RequestReceiver, - backend_sender: RequestSender, -) -> anyhow::Result<()> { - let scenario_dir = std::env::var("GAUNTLET_SCENARIOS_DIR").expect("Unable to read GAUNTLET_SCENARIOS_DIR"); - - let plugin_name = - std::env::var("GAUNTLET_SCENARIO_PLUGIN_NAME").expect("Unable to read GAUNTLET_SCENARIO_PLUGIN_NAME"); - - let scenario_dir = Path::new(&scenario_dir); - - let scenario_plugin_dir = scenario_dir - .join("plugins") - .join(&plugin_name) - .to_str() - .expect("scenario_plugin_dir is invalid UTF-8") - .to_string(); - - let scenario_data_dir = scenario_dir - .join("scenarios") - .join(&plugin_name) - .to_str() - .expect("scenario_data_dir is invalid UTF-8") - .to_string(); - - let scenario_out_dir = scenario_dir.join("out").join(&plugin_name); - - fs::create_dir_all(&scenario_out_dir).expect("unable to create scenario_out_dir"); - - let (sender, mut receiver) = tokio::sync::mpsc::channel(1); - - tokio::spawn(async move { request_loop(request_receiver, sender).await }); - - println!("waiting for backend"); - - wait_for_backend_server().await; - - println!("backend started"); - - let backend_for_frontend_client = BackendForFrontendApiProxy::new(backend_sender); - let backend_client = GrpcBackendApi::new().await?; - - println!("saving local plugin"); - - backend_client.save_local_plugin(scenario_plugin_dir.clone()).await?; - - println!("local plugin saved"); - - for entrypoint in fs::read_dir(&scenario_data_dir)? { - let entrypoint = entrypoint?; - if !entrypoint.metadata()?.is_dir() { - panic!("unexpected file {:?} at {:?}", &entrypoint, &scenario_data_dir); - } - - let entrypoint_name = entrypoint - .file_name() - .to_str() - .expect("entrypoint name is invalid UTF-8") - .to_string(); - - println!("entrypoint: {}", &entrypoint_name); - - for scenario in fs::read_dir(&entrypoint.path())? { - let scenario = scenario?; - if !scenario.metadata()?.is_file() { - panic!("unexpected file {:?} at {:?}", &scenario, &scenario_data_dir); - } - - let scenario_path = scenario.path(); - - println!("scenario: {:?}", &scenario_path); - - let scenario_name = scenario_path.file_stem().unwrap().to_str().unwrap().to_string(); - - let scenario_data = fs::read(&scenario_path).expect("unable to read scenario scenario from file"); - - let event: ScenarioBackendEvent = - serde_json::from_slice(&scenario_data).expect("unable to deserialize scenario event"); - - match event { - ScenarioBackendEvent::Search { text } => { - backend_for_frontend_client.search(text, true).await?; - } - ScenarioBackendEvent::RequestViewRender => { - let plugin_id = PluginId::from_string(format!("file://{scenario_plugin_dir}")); - let entrypoint_id = EntrypointId::from_string(&entrypoint_name); - - backend_for_frontend_client - .request_view_render(plugin_id, entrypoint_id) - .await?; - } - } - - println!("waiting for scenario to finish"); - - match receiver.recv().await { - None => unreachable!(), - Some(event) => save_event(&scenario_out_dir, scenario_name, event), - } - - println!("scenario finished"); - } - } - - println!("all scenarios done"); - - std::process::exit(0) -} - -fn save_event(scenario_out_dir: &Path, scenario_name: String, event: ScenarioFrontendEvent) { - let json = serde_json::to_string_pretty(&event).expect("unable to serialize scenario event"); - - let entrypoint_id = match event { - ScenarioFrontendEvent::ReplaceView { entrypoint_id, .. } => entrypoint_id, - ScenarioFrontendEvent::ShowPreferenceRequiredView { entrypoint_id, .. } => entrypoint_id, - ScenarioFrontendEvent::ShowPluginErrorView { entrypoint_id, .. } => entrypoint_id, - }; - - let out_dir = Path::new(scenario_out_dir).join(entrypoint_id); - - fs::create_dir_all(&out_dir).expect("Unable to create scenario out dir"); - - let out_path = out_dir.join(format!("{}.json", scenario_name)); - - fs::write(&out_path, json).expect("unable to write scenario event to file"); -} - -async fn request_loop( - mut request_receiver: RequestReceiver, - scenario_sender: tokio::sync::mpsc::Sender, -) { - loop { - let (request_data, responder) = request_receiver.recv().await; - - match request_data { - FrontendApiRequestData::UpdateLoadingBar { .. } - | FrontendApiRequestData::ShowHud { .. } - | FrontendApiRequestData::ShowWindow {} - | FrontendApiRequestData::HideWindow {} - | FrontendApiRequestData::ClearInlineView { .. } - | FrontendApiRequestData::SetTheme { .. } - | FrontendApiRequestData::OpenPluginView { .. } - | FrontendApiRequestData::OpenGeneratedPluginView { .. } => { - unreachable!() - } - FrontendApiRequestData::SetGlobalShortcut { .. } => { - // noop - responder.respond(Ok(FrontendApiResponseData::SetGlobalShortcut { data: () })); - } - FrontendApiRequestData::SetGlobalEntrypointShortcut { .. } => { - // noop - responder.respond(Ok(FrontendApiResponseData::SetGlobalEntrypointShortcut { data: () })); - } - FrontendApiRequestData::SetWindowPositionMode { .. } => { - // noop - responder.respond(Ok(FrontendApiResponseData::SetWindowPositionMode { data: () })); - } - FrontendApiRequestData::RequestSearchResultsUpdate {} => { - // noop - responder.respond(Ok(FrontendApiResponseData::RequestSearchResultsUpdate { data: () })); - } - FrontendApiRequestData::ReplaceView { - plugin_id: _, - plugin_name: _, - entrypoint_id, - entrypoint_name: _, - render_location, - top_level_view, - container, - data, - } => { - let event = ScenarioFrontendEvent::ReplaceView { - entrypoint_id: entrypoint_id.to_string(), - render_location: ui_render_location_to_scenario(render_location), - top_level_view, - container, - data, - }; - - scenario_sender.send(event).await.expect("send failed"); - responder.respond(Ok(FrontendApiResponseData::ReplaceView { data: () })); - } - FrontendApiRequestData::ShowPluginErrorView { - plugin_id: _, - entrypoint_id, - render_location, - } => { - let event = ScenarioFrontendEvent::ShowPluginErrorView { - entrypoint_id: entrypoint_id.to_string(), - render_location: ui_render_location_to_scenario(render_location), - }; - - scenario_sender.send(event).await.expect("send failed"); - responder.respond(Ok(FrontendApiResponseData::ShowPluginErrorView { data: () })); - } - FrontendApiRequestData::ShowPreferenceRequiredView { - plugin_id: _, - entrypoint_id, - plugin_preferences_required, - entrypoint_preferences_required, - } => { - let event = ScenarioFrontendEvent::ShowPreferenceRequiredView { - entrypoint_id: entrypoint_id.to_string(), - plugin_preferences_required, - entrypoint_preferences_required, - }; - - scenario_sender.send(event).await.expect("send failed"); - responder.respond(Ok(FrontendApiResponseData::ShowPreferenceRequiredView { data: () })); - } - } - } -} diff --git a/rust/scenario_runner/src/lib.rs b/rust/scenario_runner/src/lib.rs deleted file mode 100644 index 0eec05a..0000000 --- a/rust/scenario_runner/src/lib.rs +++ /dev/null @@ -1,44 +0,0 @@ -use gauntlet_common::model::UiSetupData; -use gauntlet_common::model::UiTheme; -use gauntlet_common::model::WindowPositionMode; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiRequestData; -use gauntlet_common::rpc::backend_api::BackendForFrontendApiResponseData; -use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; -use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; -use gauntlet_utils::channel::RequestReceiver; -use gauntlet_utils::channel::RequestSender; - -pub mod frontend_mock; -mod model; - -pub async fn run_scenario_runner_frontend_mock( - request_receiver: RequestReceiver, - backend_sender: RequestSender, -) -> anyhow::Result<()> { - frontend_mock::start_scenario_runner_frontend(request_receiver, backend_sender).await?; - - Ok(()) -} - -pub async fn run_scenario_runner_mock_server( - _request_sender: RequestSender, - mut backend_receiver: RequestReceiver, - theme: UiTheme, -) -> anyhow::Result<()> { - let (_data, responder) = backend_receiver.recv().await; - responder.respond(Ok(BackendForFrontendApiResponseData::SetupData { - data: UiSetupData { - window_position_file: None, - theme, - close_on_unfocus: false, - window_position_mode: WindowPositionMode::Static, - }, - })); - - let (_data, responder) = backend_receiver.recv().await; - responder.respond(Ok(BackendForFrontendApiResponseData::SetupResponse { data: () })); - - std::thread::park(); - - Ok(()) -} diff --git a/rust/scenario_runner/src/main.rs b/rust/scenario_runner/src/main.rs new file mode 100644 index 0000000..986c889 --- /dev/null +++ b/rust/scenario_runner/src/main.rs @@ -0,0 +1,24 @@ +use tracing_subscriber::EnvFilter; + +pub fn main() -> anyhow::Result<()> { + tracing_subscriber::fmt::fmt() + .with_thread_names(true) + .with_env_filter(EnvFilter::from_default_env()) + .init(); + + let scenarios_dir = std::env::var("GAUNTLET_SCENARIOS_DIR")?; + let plugins_dir = std::env::var("GAUNTLET_SCENARIOS_PLUGINS_DIR")?; + let screenshots_dir = std::env::var("GAUNTLET_SCENARIOS_SCREENSHOTS_DIR")?; + let only_plugin = std::env::var("GAUNTLET_SCENARIOS_ONLY_PLUGIN").ok(); + let only_entrypoint = std::env::var("GAUNTLET_SCENARIOS_ONLY_ENTRYPOINT").ok(); + + gauntlet_client::run_scenario( + scenarios_dir, + plugins_dir, + screenshots_dir, + only_plugin, + only_entrypoint, + ); + + Ok(()) +} diff --git a/rust/scenario_runner/src/model.rs b/rust/scenario_runner/src/model.rs deleted file mode 100644 index 9627bb7..0000000 --- a/rust/scenario_runner/src/model.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde::Deserialize; -use serde::Serialize; - -#[derive(Debug, Deserialize, Serialize)] -#[serde(tag = "type")] -pub enum ScenarioBackendEvent { - Search { text: String }, - RequestViewRender, -} diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index c34d73a..54ce89c 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -9,7 +9,6 @@ gauntlet-utils.workspace = true gauntlet-utils-macros.workspace = true gauntlet-common-plugin-runtime.workspace = true gauntlet-plugin-runtime.workspace = true -gauntlet-scenario-runner = { workspace = true, optional = true } # shared anyhow.workspace = true @@ -48,5 +47,5 @@ schemars = "0.8" [features] release = ["gauntlet-common/release", "gauntlet-plugin-runtime/release"] -scenario_runner = ["dep:gauntlet-scenario-runner", "gauntlet-common/scenario_runner", "gauntlet-plugin-runtime/scenario_runner"] +scenario_runner = ["gauntlet-common/scenario_runner", "gauntlet-plugin-runtime/scenario_runner"] diff --git a/rust/server/src/lib.rs b/rust/server/src/lib.rs index 6e7e2cd..83659e8 100644 --- a/rust/server/src/lib.rs +++ b/rust/server/src/lib.rs @@ -7,66 +7,3 @@ pub use global_hotkey; pub const PLUGIN_CONNECT_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_CONNECT__"; pub const PLUGIN_UUID_ENV: &'static str = "__GAUNTLET_INTERNAL_PLUGIN_UUID__"; - -#[cfg(feature = "scenario_runner")] -fn run_scenario_runner() { - let runner_type = - std::env::var("GAUNTLET_SCENARIO_RUNNER_TYPE").expect("Unable to read GAUNTLET_SCENARIO_RUNNER_TYPE"); - - match runner_type.as_str() { - "screenshot_gen" => { - let (frontend_sender, frontend_receiver) = channel::(); - let (backend_sender, backend_receiver) = - channel::(); - - std::thread::spawn(|| { - let theme = crate::plugins::theme::BundledThemes::new().unwrap(); - - start_mock_server(frontend_sender, backend_receiver, theme.macos_dark_theme) - }); - - start_app(false, frontend_receiver, backend_sender); - } - "scenario_runner" => { - let (frontend_sender, frontend_receiver) = channel::(); - let (backend_sender, backend_receiver) = - channel::(); - - std::thread::spawn(|| start_server(frontend_sender, backend_receiver)); - - start_frontend_mock(frontend_receiver, backend_sender); - } - _ => panic!("unknown type"), - } -} - -#[cfg(feature = "scenario_runner")] -fn start_mock_server( - request_sender: RequestSender, - backend_receiver: RequestReceiver, - theme: gauntlet_common::model::UiTheme, -) { - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .expect("unable to start server tokio runtime") - .block_on(async { - gauntlet_scenario_runner::run_scenario_runner_mock_server(request_sender, backend_receiver, theme).await - }) - .unwrap(); -} - -#[cfg(feature = "scenario_runner")] -fn start_frontend_mock( - request_receiver: RequestReceiver, - backend_sender: RequestSender, -) { - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .expect("unable to start frontend mock tokio runtime") - .block_on(async { - gauntlet_scenario_runner::run_scenario_runner_frontend_mock(request_receiver, backend_sender).await - }) - .unwrap(); -} diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index d9377e3..2c894a8 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -145,6 +145,7 @@ impl ApplicationManager { tracing::error!("error loading dev plugin: {:?}", err); } + #[cfg(not(feature = "scenario_runner"))] application_manager.reload_all_plugins()?; Ok(application_manager) @@ -598,6 +599,11 @@ impl ApplicationManager { self.settings.set_theme_setting(theme).await } + #[cfg(feature = "scenario_runner")] + pub fn get_scenarios_theme(&self) -> gauntlet_common::model::UiTheme { + self.settings.scenarios_theme() + } + pub fn get_theme(&self) -> anyhow::Result { self.settings.theme_setting() } diff --git a/rust/server/src/plugins/settings/global_shortcut.rs b/rust/server/src/plugins/settings/global_shortcut.rs index 76cf397..ac9afa2 100644 --- a/rust/server/src/plugins/settings/global_shortcut.rs +++ b/rust/server/src/plugins/settings/global_shortcut.rs @@ -427,7 +427,9 @@ pub enum GlobalShortcutAction { #[derive(Debug, Clone)] pub struct GlobalShortcutPressedEvent(u32); -pub fn register_listener + Unpin + Send + Sync + Debug + Clone + 'static>( +pub fn register_global_shortcut_listener< + S: Sink + Unpin + Send + Sync + Debug + Clone + 'static, +>( pressed_events: S, ) where >::Error: Debug, diff --git a/rust/server/src/plugins/settings/mod.rs b/rust/server/src/plugins/settings/mod.rs index 4fee0a8..f5dd11d 100644 --- a/rust/server/src/plugins/settings/mod.rs +++ b/rust/server/src/plugins/settings/mod.rs @@ -183,6 +183,11 @@ impl Settings { Ok(theme) } + #[cfg(feature = "scenario_runner")] + pub fn scenarios_theme(&self) -> UiTheme { + self.themes.macos_dark_theme.clone() + } + pub fn theme_setting(&self) -> anyhow::Result { if let Some(_) = read_theme_file(self.dirs.theme_file()) { return Ok(SettingsTheme::ThemeFile); diff --git a/rust/utils/Cargo.toml b/rust/utils/Cargo.toml index 0417522..394194c 100644 --- a/rust/utils/Cargo.toml +++ b/rust/utils/Cargo.toml @@ -8,3 +8,5 @@ thiserror.workspace = true anyhow.workspace = true tonic.workspace = true prost.workspace = true +x11rb-protocol.workspace = true +x11rb-async.workspace = true diff --git a/rust/utils/src/lib.rs b/rust/utils/src/lib.rs index ff02972..d6011df 100644 --- a/rust/utils/src/lib.rs +++ b/rust/utils/src/lib.rs @@ -1 +1,3 @@ pub mod channel; +pub mod x11rb_async_atom_manager; +pub mod x11rb_async_wm_class; diff --git a/rust/utils/src/x11rb_async_atom_manager.rs b/rust/utils/src/x11rb_async_atom_manager.rs new file mode 100644 index 0000000..5ba7d88 --- /dev/null +++ b/rust/utils/src/x11rb_async_atom_manager.rs @@ -0,0 +1,84 @@ +pub use x11rb_protocol::x11_utils::BigRequests; +pub use x11rb_protocol::x11_utils::ExtInfoProvider; +pub use x11rb_protocol::x11_utils::ExtensionInformation; +pub use x11rb_protocol::x11_utils::ReplyParsingFunction; +pub use x11rb_protocol::x11_utils::Request; +pub use x11rb_protocol::x11_utils::RequestHeader; +pub use x11rb_protocol::x11_utils::Serialize; +pub use x11rb_protocol::x11_utils::TryParse; +pub use x11rb_protocol::x11_utils::TryParseFd; +pub use x11rb_protocol::x11_utils::X11Error; +pub use x11rb_protocol::x11_utils::parse_request_header; + +#[macro_export] +macro_rules! atom_manager { + { + $(#[$struct_meta:meta])* + $vis:vis $struct_name:ident: + $(#[$cookie_meta:meta])* + $cookie_name:ident { + $($field_name:ident$(: $atom_value:expr)?,)* + } + } => { + // Cookie version + #[allow(non_snake_case)] + #[derive(Debug)] + $(#[$cookie_meta])* + $vis struct $cookie_name<'a, C: x11rb_async::connection::Connection> { + __private_phantom: ::std::marker::PhantomData<&'a C>, + __private_cookies: ::std::vec::Vec>, + } + + // Replies + #[allow(non_snake_case)] + #[derive(Debug, Clone, Copy)] + $(#[$struct_meta])* + $vis struct $struct_name { + $( + $vis $field_name: x11rb_async::protocol::xproto::Atom, + )* + } + + impl $struct_name { + $vis async fn new( + _conn: &C, + ) -> ::std::result::Result<$cookie_name<'_, C>, x11rb_async::errors::ConnectionError> { + use futures::stream::{self, StreamExt, TryStreamExt}; + use x11rb_async::protocol::xproto::ConnectionExt; + let names = futures::stream::iter(vec![ + $($crate::__atom_manager_atom_value!($field_name$(: $atom_value)?),)* + ]); + let cookies: ::std::result::Result<::std::vec::Vec<_>, _> = names + .then(|name| _conn.intern_atom(false, name)) + .try_collect() + .await; + Ok($cookie_name { + __private_phantom: ::std::marker::PhantomData, + __private_cookies: cookies?, + }) + } + } + + impl<'a, C: x11rb_async::connection::Connection> $cookie_name<'a, C> { + $vis async fn reply(self) -> ::std::result::Result<$struct_name, x11rb_async::errors::ReplyError> { + let mut replies = self.__private_cookies.into_iter(); + Ok($struct_name { + $( + $field_name: replies.next().expect("new() should have constructed a Vec of the correct size").reply().await?.atom, + )* + }) + } + } + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __atom_manager_atom_value { + ($field_name:ident) => { + stringify!($field_name).as_bytes() + }; + ($field_name:ident: $atom_value:expr) => { + $atom_value + }; +} diff --git a/rust/utils/src/x11rb_async_wm_class.rs b/rust/utils/src/x11rb_async_wm_class.rs new file mode 100644 index 0000000..2db248b --- /dev/null +++ b/rust/utils/src/x11rb_async_wm_class.rs @@ -0,0 +1,109 @@ +use x11rb_async::Cookie; +use x11rb_async::connection::Connection; +use x11rb_async::errors::ConnectionError; +use x11rb_async::errors::ParseError; +use x11rb_async::errors::ReplyError; +use x11rb_async::protocol::xproto::AtomEnum; +use x11rb_async::protocol::xproto::GetPropertyReply; +use x11rb_async::protocol::xproto::Window; +use x11rb_async::protocol::xproto::{self}; + +macro_rules! property_cookie { + { + $(#[$meta:meta])* + pub struct $cookie_name:ident: $struct_name:ident, + $from_reply:expr, + } => { + $(#[$meta])* + #[derive(Debug)] + pub struct $cookie_name<'a, Conn: Connection + ?Sized>(Cookie<'a, Conn, GetPropertyReply>); + + impl<'a, Conn> $cookie_name<'a, Conn> + where + Conn: Connection + ?Sized, + { + /// Get the reply that the server sent. + pub async fn reply(self) -> Result, ReplyError> { + #[allow(clippy::redundant_closure_call)] + Ok($from_reply(self.0.reply().await?)?) + } + + /// Get the reply that the server sent, but have errors handled as events. + pub async fn reply_unchecked(self) -> Result, ConnectionError> { + self.0 + .reply_unchecked().await? + .map($from_reply) + .transpose() + .map(|e| e.flatten()) + .map_err(Into::into) + } + } + } +} + +// WM_CLASS + +property_cookie! { + /// A cookie for getting a window's `WM_CLASS` property. + /// + /// See `WmClass`. + pub struct WmClassCookie: WmClass, + WmClass::from_reply, +} + +impl<'a, Conn> WmClassCookie<'a, Conn> +where + Conn: Connection + ?Sized, +{ + /// Send a `GetProperty` request for the `WM_CLASS` property of the given window + pub async fn new(conn: &'a Conn, window: Window) -> Result { + Ok(Self( + xproto::get_property(conn, false, window, AtomEnum::WM_CLASS, AtomEnum::STRING, 0, 2048).await?, + )) + } +} + +#[derive(Debug)] +pub struct WmClass(GetPropertyReply, usize); + +impl WmClass { + /// Send a `GetProperty` request for the `WM_CLASS` property of the given window + pub async fn get(conn: &C, window: Window) -> Result, ConnectionError> { + WmClassCookie::new(conn, window).await + } + + /// Construct a new `WmClass` instance from a `GetPropertyReply`. + /// + /// The original `GetProperty` request must have been for a `WM_CLASS` property for this + /// function to return sensible results. + pub fn from_reply(reply: GetPropertyReply) -> Result, ParseError> { + if reply.type_ == AtomEnum::NONE.into() { + return Ok(None); + } + if reply.type_ != AtomEnum::STRING.into() || reply.format != 8 { + return Err(ParseError::InvalidValue); + } + // Find the first zero byte in the value + let offset = reply.value.iter().position(|&v| v == 0).unwrap_or(reply.value.len()); + Ok(Some(WmClass(reply, offset))) + } + + /// Get the instance contained in this `WM_CLASS` property + pub fn instance(&self) -> &[u8] { + &self.0.value[0..self.1] + } + + /// Get the class contained in this `WM_CLASS` property + pub fn class(&self) -> &[u8] { + let start = self.1 + 1; + if start >= self.0.value.len() { + return &[]; + }; + let end = if self.0.value.last() == Some(&0) { + self.0.value.len() - 1 + } else { + self.0.value.len() + }; + &self.0.value[start..end] + } +} From 9111dd074be82cc68d3dea43a853440df9926a45 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 24 Jun 2025 20:57:43 +0200 Subject: [PATCH 44/91] Fix build on non-linux systems --- rust/client/src/ui/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 55bbcef..bd78d93 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -298,7 +298,6 @@ pub fn run(minimized: bool, scenario_runner_data: Option) { #[cfg(not(target_os = "linux"))] let result = run_non_wayland( minimized, - #[cfg(feature = "scenario_runner")] scenario_runner_data, ); From 70e815b7954b03d5754c2c4158eaa78ce6a9109d Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 24 Jun 2025 21:00:23 +0200 Subject: [PATCH 45/91] Fix formatting --- rust/client/src/ui/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index bd78d93..25519b5 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -296,10 +296,7 @@ pub fn run(minimized: bool, scenario_runner_data: Option) { }; #[cfg(not(target_os = "linux"))] - let result = run_non_wayland( - minimized, - scenario_runner_data, - ); + let result = run_non_wayland(minimized, scenario_runner_data); result.expect("Unable to start application") } From 283285c317c42623c8178a7b4c4493d0015b302e Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 1 Jul 2025 23:24:22 +0200 Subject: [PATCH 46/91] Replace iced_layer shell with layer_shell implementation from winit fork --- Cargo.lock | 210 ++----------------- Cargo.toml | 5 +- README.md | 3 - rust/client/Cargo.toml | 1 - rust/client/src/ui/mod.rs | 94 +++------ rust/client/src/ui/scenario_runner.rs | 5 +- rust/client/src/ui/windows/hud.rs | 57 ++---- rust/client/src/ui/windows/mod.rs | 281 ++++++++------------------ 8 files changed, 153 insertions(+), 503 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62475b0..69b62a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1237,38 +1237,13 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "calloop" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10929724661d1c43856fd87c7a127ae944ec55579134fb485e4136fb6a46fdcb" -dependencies = [ - "bitflags 2.9.1", - "polling", - "rustix 0.38.44", - "slab", - "tracing", -] - [[package]] name = "calloop-wayland-source" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "calloop 0.13.0", - "rustix 0.38.44", - "wayland-backend", - "wayland-client", -] - -[[package]] -name = "calloop-wayland-source" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a7a1dbbe026a55ef47a500b123af5a9a0914520f061d467914cf21be95daf" -dependencies = [ - "calloop 0.14.2", + "calloop", "rustix 0.38.44", "wayland-backend", "wayland-client", @@ -3536,7 +3511,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.8", + "libloading 0.7.4", ] [[package]] @@ -4551,7 +4526,6 @@ dependencies = [ "gauntlet-utils", "iced", "iced_fonts", - "iced_layershell", "image", "itertools 0.13.0", "objc2-app-kit 0.2.2", @@ -5786,7 +5760,7 @@ dependencies = [ [[package]] name = "iced" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "iced_core", "iced_debug", @@ -5802,7 +5776,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "bitflags 2.9.1", "bytes", @@ -5820,35 +5794,13 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "iced_core", "iced_futures", "log", ] -[[package]] -name = "iced_devtools" -version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" -dependencies = [ - "iced_debug", - "iced_program", - "iced_widget", - "log", -] - -[[package]] -name = "iced_exdevtools" -version = "0.13.99" -source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13.1#1f14198177efc2139f09d1b3bd6294c3f4673925" -dependencies = [ - "iced_debug", - "iced_program", - "iced_widget", - "log", -] - [[package]] name = "iced_fonts" version = "0.2.99" @@ -5873,7 +5825,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "futures", "iced_core", @@ -5887,7 +5839,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "bitflags 2.9.1", "bytemuck", @@ -5904,47 +5856,10 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "iced_layershell" -version = "0.13.99" -source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13.1#1f14198177efc2139f09d1b3bd6294c3f4673925" -dependencies = [ - "enumflags2", - "futures", - "iced", - "iced_core", - "iced_debug", - "iced_devtools", - "iced_exdevtools", - "iced_futures", - "iced_graphics", - "iced_layershell_macros", - "iced_program", - "iced_renderer", - "iced_runtime", - "layershellev", - "log", - "thiserror 2.0.12", - "tracing", - "window_clipboard", -] - -[[package]] -name = "iced_layershell_macros" -version = "0.13.99" -source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13.1#1f14198177efc2139f09d1b3bd6294c3f4673925" -dependencies = [ - "darling", - "manyhow", - "proc-macro2", - "quote", - "syn 2.0.103", -] - [[package]] name = "iced_program" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "iced_graphics", "iced_runtime", @@ -5953,7 +5868,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -5965,7 +5880,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "bytes", "iced_core", @@ -5978,7 +5893,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "bytemuck", "cosmic-text", @@ -5995,7 +5910,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "bitflags 2.9.1", "bytemuck", @@ -6015,7 +5930,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "iced_renderer", "iced_runtime", @@ -6030,7 +5945,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#39f56309f4624c20218b812a70d6a7c0e7e698e8" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" dependencies = [ "iced_debug", "iced_program", @@ -6670,27 +6585,6 @@ dependencies = [ "log", ] -[[package]] -name = "layershellev" -version = "0.13.99" -source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13.1#1f14198177efc2139f09d1b3bd6294c3f4673925" -dependencies = [ - "bitflags 2.9.1", - "calloop 0.14.2", - "calloop-wayland-source 0.4.0", - "log", - "raw-window-handle", - "tempfile", - "thiserror 2.0.12", - "waycrate_xkbkeycode", - "wayland-backend", - "wayland-client", - "wayland-cursor", - "wayland-protocols", - "wayland-protocols-misc", - "wayland-protocols-wlr", -] - [[package]] name = "lazy-regex" version = "3.4.1" @@ -7016,30 +6910,6 @@ dependencies = [ "libc", ] -[[package]] -name = "manyhow" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587" -dependencies = [ - "darling_core", - "manyhow-macros", - "proc-macro2", - "quote", - "syn 2.0.103", -] - -[[package]] -name = "manyhow-macros" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495" -dependencies = [ - "proc-macro-utils", - "proc-macro2", - "quote", -] - [[package]] name = "matchers" version = "0.1.0" @@ -7628,7 +7498,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.103", @@ -8857,17 +8727,6 @@ dependencies = [ "syn 2.0.103", ] -[[package]] -name = "proc-macro-utils" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" -dependencies = [ - "proc-macro2", - "quote", - "smallvec", -] - [[package]] name = "proc-macro2" version = "1.0.95" @@ -10410,8 +10269,8 @@ checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ "bitflags 2.9.1", "bytemuck", - "calloop 0.13.0", - "calloop-wayland-source 0.3.0", + "calloop", + "calloop-wayland-source", "cursor-icon", "libc", "log", @@ -11830,7 +11689,6 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -12571,21 +12429,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "waycrate_xkbkeycode" -version = "0.13.99" -source = "git+https://github.com/project-gauntlet/exwlshelleventloop.git?branch=gauntlet-0.13.1#1f14198177efc2139f09d1b3bd6294c3f4673925" -dependencies = [ - "bitflags 2.9.1", - "calloop 0.14.2", - "log", - "memmap2 0.9.5", - "smol_str", - "wayland-backend", - "wayland-client", - "xkbcommon-dl", -] - [[package]] name = "wayland-backend" version = "0.3.10" @@ -12647,19 +12490,6 @@ dependencies = [ "wayland-server", ] -[[package]] -name = "wayland-protocols-misc" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "635cf2968bd88599445b25a2eeef655d463bb04f9aed04e4bf8c2018f3d4fc41" -dependencies = [ - "bitflags 2.9.1", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", -] - [[package]] name = "wayland-protocols-plasma" version = "0.3.8" @@ -12972,7 +12802,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -13460,7 +13290,7 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winit" version = "0.30.99" -source = "git+https://github.com/project-gauntlet/winit.git?rev=49690da86351375d2e2ebe8b891cdb90a019b996#49690da86351375d2e2ebe8b891cdb90a019b996" +source = "git+https://github.com/project-gauntlet/winit.git?rev=e9023f4f0434ba86dae50b1e92e53e565bef82ab#e9023f4f0434ba86dae50b1e92e53e565bef82ab" dependencies = [ "ahash 0.8.12", "android-activity", @@ -13468,7 +13298,7 @@ dependencies = [ "bitflags 2.9.1", "block2 0.5.1", "bytemuck", - "calloop 0.13.0", + "calloop", "cfg_aliases", "concurrent-queue", "core-foundation 0.9.4", diff --git a/Cargo.toml b/Cargo.toml index a9b129f..6e3f43e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,6 @@ edition = "2024" iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet-0.13.1", features = ["wgpu", "tiny-skia", "web-colors", "tokio", "lazy", "advanced", "image", "svg"] } #iced_fonts = { version = "0.2.99", features = ["bootstrap"] } iced_fonts = { git = "https://github.com/project-gauntlet/iced_fonts.git", branch = "gauntlet-0.13.1", features = ["bootstrap"] } -#iced_layershell = "0.13.99" -iced_layershell = { git = "https://github.com/project-gauntlet/exwlshelleventloop.git", branch = "gauntlet-0.13.1" } # workspaces gauntlet-common = { path = "./rust/common" } @@ -89,7 +87,6 @@ strip = true #iced_fonts = { path = "../../../RustroverProjects/iced_fonts_fork" } #iced = { path = "../../../RustroverProjects/iced-fork" } #iced_debug = { path = "../../../RustroverProjects/iced-fork/debug" } -#iced_devtools = { path = "../../../RustroverProjects/iced-fork/devtools" } #iced_program = { path = "../../../RustroverProjects/iced-fork/program" } #iced_core = { path = "../../../RustroverProjects/iced-fork/core" } #iced_futures = { path = "../../../RustroverProjects/iced-fork/futures" } @@ -100,4 +97,4 @@ strip = true #iced_wgpu = { path = "../../../RustroverProjects/iced-fork/wgpu" } #iced_widget = { path = "../../../RustroverProjects/iced-fork/widget" } #iced_winit = { path = "../../../RustroverProjects/iced-fork/winit" } -#iced_layershell = { path = "../../../RustroverProjects/exwlshelleventloop/iced_layershell" } +#winit = { path = "../../../RustroverProjects/winit" } diff --git a/README.md b/README.md index f0bbc77..b811762 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,3 @@ If you'd like to help build Gauntlet you can do it in more ways than just contri For simple problems feel free to open an issue or PR and tackle it yourself. For more significant changes please contact creators on Discord (invite link on top of README) and discuss first. - -All and any contributions are welcome. - diff --git a/rust/client/Cargo.toml b/rust/client/Cargo.toml index 305186e..95ac512 100644 --- a/rust/client/Cargo.toml +++ b/rust/client/Cargo.toml @@ -30,7 +30,6 @@ arc-swap = "1.7.1" tray-icon = { version = "0.19.2", default-features = false } [target.'cfg(target_os = "linux")'.dependencies] -iced_layershell.workspace = true x11rb-async.workspace = true [target.'cfg(target_os = "macos")'.dependencies] diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 25519b5..b7bc0ed 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -100,7 +100,6 @@ use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::root::render_root; use crate::ui::windows::WindowActionMsg; use crate::ui::windows::WindowState; -use crate::ui::windows::create_window; #[cfg(target_os = "linux")] use crate::ui::windows::x11_focus::x11_linux_focus_change_subscription; @@ -282,34 +281,7 @@ pub enum AppMsg { } pub fn run(minimized: bool, scenario_runner_data: Option) { - #[cfg(target_os = "linux")] - let result = { - let wayland = std::env::var("WAYLAND_DISPLAY") - .or_else(|_| std::env::var("WAYLAND_SOCKET")) - .is_ok(); - - if wayland { - run_wayland(minimized, scenario_runner_data) - } else { - run_non_wayland(minimized, scenario_runner_data) - } - }; - - #[cfg(not(target_os = "linux"))] - let result = run_non_wayland(minimized, scenario_runner_data); - - result.expect("Unable to start application") -} - -fn run_non_wayland(minimized: bool, scenario_runner_data: Option) -> anyhow::Result<()> { - let boot = move || { - new( - #[cfg(target_os = "linux")] - false, - minimized, - scenario_runner_data.clone(), - ) - }; + let boot = move || new(minimized, scenario_runner_data.clone()); iced::daemon::(boot, update, view) .title(title) @@ -324,51 +296,29 @@ fn run_non_wayland(minimized: bool, scenario_runner_data: Option) -> anyhow::Result<()> { - let boot = move || new(true, minimized, scenario_runner_data.clone()); - - iced_layershell::build_pattern::daemon(boot, "gauntlet", update, view) - .layer_settings(iced_layershell::settings::LayerShellSettings { - start_mode: iced_layershell::settings::StartMode::Background, - events_transparent: true, - keyboard_interactivity: iced_layershell::reexport::KeyboardInteractivity::None, - size: None, - ..Default::default() - }) - .subscription(subscription) - .theme(|state, _| state.theme.clone()) - .run()?; - - Ok(()) -} - -fn new( - #[cfg(target_os = "linux")] wayland: bool, - minimized: bool, - #[allow(unused)] scenario_runner_data: Option, -) -> (AppModel, Task) { +fn new(minimized: bool, #[allow(unused)] scenario_runner_data: Option) -> (AppModel, Task) { let (application_manager, global_hotkey_manager, setup_data, setup_task) = server::setup(); + #[cfg(target_os = "linux")] + let wayland = std::env::var("WAYLAND_DISPLAY") + .or_else(|_| std::env::var("WAYLAND_SOCKET")) + .is_ok(); // todo add config value for layer shell + let theme = GauntletComplexTheme::new(setup_data.theme); GauntletComplexTheme::set_global(theme.clone()); - let (main_window_id, open_task) = create_window( - #[cfg(target_os = "linux")] - wayland, - minimized, - setup_data.window_position_file.as_ref(), - ); - let mut tasks: Vec> = vec![]; - tasks.push(open_task.map(AppMsg::WindowAction)); + tasks.push(setup_task); + if !minimized { + tasks.push(Task::done(AppMsg::WindowAction(WindowActionMsg::ShowWindow))); + } + #[cfg(feature = "scenario_runner")] tasks.push(scenario_runner::run_scenario( scenario_runner_data.unwrap(), @@ -378,8 +328,6 @@ fn new( let client_context = ClientContext::new(); let global_state = GlobalState::new(text_input::Id::unique()); let window = WindowState::new( - main_window_id, - minimized, setup_data.window_position_file, setup_data.close_on_unfocus, setup_data.window_position_mode, @@ -412,7 +360,7 @@ fn new( } fn title(state: &AppModel, window: window::Id) -> String { - if window == state.window.main_window_id { + if Some(window) == state.window.main_window_id { "Gauntlet".to_owned() } else { "Gauntlet HUD".to_owned() @@ -692,7 +640,11 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } AppMsg::IcedEvent(window_id, Event::Keyboard(event)) => { - if window_id != state.window.main_window_id { + let Some(main_window_id) = state.window.main_window_id else { + return Task::none(); + }; + + if window_id != main_window_id { return Task::none(); } @@ -1232,10 +1184,10 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } fn view(state: &AppModel, window: window::Id) -> Element<'_, AppMsg> { - if window != state.window.main_window_id { - view_hud(state) - } else { + if Some(window) == state.window.main_window_id { view_main(state) + } else { + view_hud(state) } } diff --git a/rust/client/src/ui/scenario_runner.rs b/rust/client/src/ui/scenario_runner.rs index 396914c..667c5fe 100644 --- a/rust/client/src/ui/scenario_runner.rs +++ b/rust/client/src/ui/scenario_runner.rs @@ -44,7 +44,7 @@ pub enum ScenarioRunnerMsg { pub fn handle_scenario_runner_msg( msg: ScenarioRunnerMsg, application_manager: Arc, - main_window_id: window::Id, + main_window_id: Option, ) -> Task { match msg { ScenarioRunnerMsg::AddScenarioPlugin { plugin_path } => { @@ -55,7 +55,8 @@ pub fn handle_scenario_runner_msg( Task::none() } ScenarioRunnerMsg::Screenshot { save_path } => { - window::screenshot(main_window_id).map(move |screenshot| { + let id = main_window_id.expect("Main window should be open at the time of making screenshot"); + window::screenshot(id).map(move |screenshot| { ScenarioRunnerMsg::ScreenshotDone { save_path: save_path.clone(), screenshot, diff --git a/rust/client/src/ui/windows/hud.rs b/rust/client/src/ui/windows/hud.rs index c35657b..e2a6306 100644 --- a/rust/client/src/ui/windows/hud.rs +++ b/rust/client/src/ui/windows/hud.rs @@ -14,18 +14,6 @@ const HUD_WINDOW_WIDTH: f32 = 400.0; const HUD_WINDOW_HEIGHT: f32 = 40.0; pub fn show_hud_window(#[cfg(target_os = "linux")] wayland: bool) -> Task { - #[cfg(target_os = "linux")] - if wayland { - open_wayland() - } else { - open_non_wayland() - } - - #[cfg(not(target_os = "linux"))] - open_non_wayland() -} - -fn open_non_wayland() -> Task { let settings = Settings { size: Size::new(HUD_WINDOW_WIDTH, HUD_WINDOW_HEIGHT), position: Position::SpecificWith(|window, screen| { @@ -44,6 +32,15 @@ fn open_non_wayland() -> Task { window_kind: window::settings::WindowKind::Panel, ..Default::default() }, + #[cfg(target_os = "linux")] + platform_specific: window::settings::PlatformSpecific { + layer_shell: if wayland { + layer_shell_settings() + } else { + Default::default() + }, + ..Default::default() + }, exit_on_close_request: false, ..Default::default() }; @@ -54,36 +51,16 @@ fn open_non_wayland() -> Task { } #[cfg(target_os = "linux")] -fn open_wayland() -> Task { - use crate::ui::windows::layer_shell; - - let id = window::Id::unique(); - let settings = layer_shell_settings(); - - Task::batch([ - Task::done(WindowActionMsg::LayerShell( - layer_shell::LayerShellAppMsg::NewLayerShell { id, settings }, - )), - sleep_for_2_seconds(id).then(|id| { - Task::done(WindowActionMsg::LayerShell( - layer_shell::LayerShellAppMsg::RemoveWindow(id), - )) - }), - ]) -} - -#[cfg(target_os = "linux")] -fn layer_shell_settings() -> iced_layershell::reexport::NewLayerShellSettings { - iced_layershell::reexport::NewLayerShellSettings { - layer: iced_layershell::reexport::Layer::Overlay, - keyboard_interactivity: iced_layershell::reexport::KeyboardInteractivity::None, - use_last_output: false, - events_transparent: true, - anchor: iced_layershell::reexport::Anchor::empty(), - margin: Default::default(), +fn layer_shell_settings() -> window::settings::LayerShellSettings { + window::settings::LayerShellSettings { + layer: Some(window::settings::Layer::Overlay), + keyboard_interactivity: Some(window::settings::KeyboardInteractivity::None), + anchor: None, + margin: None, exclusive_zone: Some(0), - size: Some((HUD_WINDOW_WIDTH as u32, HUD_WINDOW_HEIGHT as u32)), namespace: Some("gauntlet-hud".to_string()), + output: None, + input_region: Some((0, 0, 0, 0)), // mouse events transparency } } diff --git a/rust/client/src/ui/windows/mod.rs b/rust/client/src/ui/windows/mod.rs index 71d6be1..3332cd5 100644 --- a/rust/client/src/ui/windows/mod.rs +++ b/rust/client/src/ui/windows/mod.rs @@ -7,7 +7,6 @@ use iced::Size; use iced::Task; use iced::window; use iced::window::Level; -use iced::window::Mode; use iced::window::Position; use crate::ui::AppMsg; @@ -18,9 +17,8 @@ pub mod hud; pub mod x11_focus; pub struct WindowState { - pub main_window_id: window::Id, + pub main_window_id: Option, focused: bool, - opened: bool, #[cfg(target_os = "linux")] pub wayland: bool, window_position_mode: WindowPositionMode, @@ -28,21 +26,36 @@ pub struct WindowState { window_position_file: Option, #[cfg(target_os = "linux")] x11_active_window: Option, + open_position: Position, } impl WindowState { pub fn new( - main_window_id: window::Id, - minimized: bool, window_position_file: Option, close_on_unfocus: bool, window_position_mode: WindowPositionMode, #[cfg(target_os = "linux")] wayland: bool, ) -> WindowState { + let open_position = window_position_file + .as_ref() + .map(|window_position_file| fs::read_to_string(window_position_file).ok()) + .flatten() + .map(|data| { + if let Some((x, y)) = data.split_once(":") { + match (x.parse(), y.parse()) { + (Ok(x), Ok(y)) => Some(Position::Specific(Point::new(x, y))), + _ => None, + } + } else { + None + } + }) + .unwrap_or(None) + .unwrap_or(Position::Centered); + Self { - main_window_id, + main_window_id: None, focused: false, - opened: !minimized, #[cfg(target_os = "linux")] wayland, window_position_mode, @@ -50,6 +63,7 @@ impl WindowState { window_position_file, #[cfg(target_os = "linux")] x11_active_window: None, + open_position, } } } @@ -57,11 +71,6 @@ impl WindowState { impl WindowState { pub fn handle_action(&mut self, action: WindowActionMsg) -> Task { match action { - #[cfg(target_os = "linux")] - WindowActionMsg::LayerShell(_) => { - // handled by library - Task::none() - } WindowActionMsg::SetWindowPositionMode { mode } => { self.window_position_mode = mode; @@ -96,6 +105,10 @@ impl WindowState { Task::batch([Task::done(AppMsg::SetHudDisplay { display }), show_hud]) } + WindowActionMsg::SetMainWindowId(id) => { + self.main_window_id = id; + Task::none() + } } } pub fn handle_unfocused_event(&mut self, window_id: window::Id) -> Task { @@ -103,7 +116,11 @@ impl WindowState { return Task::none(); } - if window_id != self.main_window_id { + let Some(main_window_id) = self.main_window_id else { + return Task::none(); + }; + + if window_id != main_window_id { return Task::none(); } @@ -124,7 +141,11 @@ impl WindowState { return Task::none(); } - if window_id != self.main_window_id { + let Some(main_window_id) = self.main_window_id else { + return Task::none(); + }; + + if window_id != main_window_id { return Task::none(); } @@ -132,7 +153,11 @@ impl WindowState { } pub fn handle_move_event(&mut self, window_id: window::Id, point: Point) -> Task { - if window_id != self.main_window_id { + let Some(main_window_id) = self.main_window_id else { + return Task::none(); + }; + + if window_id != main_window_id { return Task::none(); } @@ -141,6 +166,8 @@ impl WindowState { let _ = fs::write(&window_position_file, format!("{}:{}", point.x, point.y)); } + self.open_position = Position::Specific(Point::new(point.x, point.y)); + Task::none() } @@ -151,7 +178,7 @@ impl WindowState { #[allow(unused)] fn on_unfocused(&mut self) -> Task { - // for some reason (on both macOS and linux x11 but x11 now uses separate impl) duplicate Unfocused fires right before Focus event + // for some reason (on both macOS and linux x11, but x11 now uses separate impl) duplicate Unfocused fires right before Focus event if self.focused { self.hide_window(true) } else { @@ -160,31 +187,23 @@ impl WindowState { } fn toggle_window(&mut self) -> Task { - if self.opened { - self.hide_window(false) - } else { - self.show_window() + match self.main_window_id { + Some(_) => self.hide_window(false), + None => self.show_window(), } } fn hide_window(&mut self, reset_state: bool) -> Task { - if !self.opened { + let Some(main_window_id) = self.main_window_id else { return Task::none(); - } + }; self.focused = false; - self.opened = false; let mut commands = vec![]; - commands.push( - hide_window( - #[cfg(target_os = "linux")] - self.wayland, - self.main_window_id, - ) - .map(AppMsg::WindowAction), - ); + commands.push(window::close(main_window_id)); + commands.push(Task::done(AppMsg::WindowAction(WindowActionMsg::SetMainWindowId(None)))); if reset_state { commands.push(Task::done(AppMsg::ClosePluginView)); @@ -193,31 +212,40 @@ impl WindowState { commands.push(Task::done(AppMsg::ResetMainWindowScroll)); + #[cfg(target_os = "macos")] + macos_focus_previous_app(); + Task::batch(commands) } fn show_window(&mut self) -> Task { - if self.opened { + if let Some(_) = self.main_window_id { return Task::none(); - } + }; - self.opened = true; - - show_window( - self.main_window_id, + let (main_window_id, open_task) = window::open(window_settings( #[cfg(target_os = "linux")] self.wayland, + self.open_position, + )); + + Task::batch([ + open_task.map(|id| WindowActionMsg::SetMainWindowId(Some(id))), #[cfg(target_os = "macos")] - &self.window_position_mode, - ) + match self.window_position_mode { + WindowPositionMode::Static => Task::none(), + WindowPositionMode::ActiveMonitor => window::move_to_active_monitor(main_window_id), + }, + window::gain_focus(main_window_id), + window::set_level(main_window_id, Level::AlwaysOnTop), + ]) .map(AppMsg::WindowAction) } } #[derive(Debug, Clone)] pub enum WindowActionMsg { - #[cfg(target_os = "linux")] - LayerShell(layer_shell::LayerShellAppMsg), + SetMainWindowId(Option), SetWindowPositionMode { mode: WindowPositionMode, }, @@ -234,44 +262,28 @@ pub enum WindowActionMsg { }, } -#[cfg(target_os = "linux")] -mod layer_shell { - #[iced_layershell::to_layer_message(multi)] - #[derive(Debug, Clone)] - pub enum LayerShellAppMsg {} -} - -#[cfg(target_os = "linux")] -impl TryInto for AppMsg { - type Error = Self; - fn try_into(self) -> Result { - match self { - Self::WindowAction(WindowActionMsg::LayerShell(msg)) => { - msg.try_into() - .map_err(|msg| Self::WindowAction(WindowActionMsg::LayerShell(msg))) - } - _ => Err(self), - } - } -} - const WINDOW_WIDTH: f32 = 750.0; const WINDOW_HEIGHT: f32 = 450.0; #[cfg(not(target_os = "macos"))] -fn window_settings(visible: bool, position: Position) -> window::Settings { +fn window_settings(#[cfg(target_os = "linux")] wayland: bool, position: Position) -> window::Settings { window::Settings { size: Size::new(WINDOW_WIDTH, WINDOW_HEIGHT), position, resizable: false, decorations: false, - visible, + visible: true, transparent: true, closeable: false, minimizable: false, #[cfg(target_os = "linux")] platform_specific: window::settings::PlatformSpecific { application_id: "gauntlet".to_string(), + layer_shell: if wayland { + layer_shell_settings() + } else { + Default::default() + }, ..Default::default() }, ..Default::default() @@ -279,13 +291,13 @@ fn window_settings(visible: bool, position: Position) -> window::Settings { } #[cfg(target_os = "macos")] -fn window_settings(visible: bool, position: Position) -> window::Settings { +fn window_settings(position: Position) -> window::Settings { window::Settings { size: Size::new(WINDOW_WIDTH, WINDOW_HEIGHT), position, resizable: false, decorations: true, - visible, + visible: true, transparent: false, closeable: false, minimizable: false, @@ -300,76 +312,8 @@ fn window_settings(visible: bool, position: Position) -> window::Settings { } } -pub fn create_window( - #[cfg(target_os = "linux")] wayland: bool, - minimized: bool, - window_position_file: Option<&PathBuf>, -) -> (window::Id, Task) { - #[cfg(target_os = "linux")] - let (main_window_id, open_task) = if wayland { - let id = window::Id::unique(); - - if minimized { - (id, Task::none()) - } else { - open_main_window_wayland(id) - } - } else { - open_main_window_non_wayland(minimized, window_position_file) - }; - - #[cfg(not(target_os = "linux"))] - let (main_window_id, open_task) = open_main_window_non_wayland(minimized, window_position_file); - - (main_window_id, open_task) -} - -pub fn show_window( - main_window_id: window::Id, - #[cfg(target_os = "linux")] wayland: bool, - #[cfg(target_os = "macos")] window_position_mode: &WindowPositionMode, -) -> Task { - #[cfg(target_os = "linux")] - let open_task = if wayland { - let (_, open_task) = open_main_window_wayland(main_window_id); - open_task - } else { - Task::batch([ - window::gain_focus(main_window_id), - window::set_mode(main_window_id, Mode::Windowed), - ]) - }; - - #[cfg(not(target_os = "linux"))] - let open_task = Task::batch([ - window::gain_focus(main_window_id), - #[cfg(target_os = "macos")] - match window_position_mode { - WindowPositionMode::Static => Task::none(), - WindowPositionMode::ActiveMonitor => window::move_to_active_monitor(main_window_id), - }, - window::set_mode(main_window_id, Mode::Windowed), - ]); - - open_task -} - -pub fn hide_window(#[cfg(target_os = "linux")] wayland: bool, main_window_id: window::Id) -> Task { - let mut commands = vec![]; - - #[cfg(target_os = "linux")] - if wayland { - commands.push(Task::done(WindowActionMsg::LayerShell( - layer_shell::LayerShellAppMsg::RemoveWindow(main_window_id), - ))); - } else { - commands.push(window::set_mode(main_window_id, Mode::Hidden)); - }; - - #[cfg(not(target_os = "linux"))] - commands.push(window::set_mode(main_window_id, Mode::Hidden)); - - #[cfg(target_os = "macos")] +#[cfg(target_os = "macos")] +pub fn macos_focus_previous_app() -> Task { unsafe { // when closing NSPanel current active application doesn't automatically become key window // is there a proper way? without doing this manually @@ -379,65 +323,18 @@ pub fn hide_window(#[cfg(target_os = "linux")] wayland: bool, main_window_id: wi app.activateWithOptions(objc2_app_kit::NSApplicationActivationOptions::empty()); } } - - Task::batch(commands) } #[cfg(target_os = "linux")] -fn layer_shell_settings() -> iced_layershell::reexport::NewLayerShellSettings { - iced_layershell::reexport::NewLayerShellSettings { - layer: iced_layershell::reexport::Layer::Overlay, - keyboard_interactivity: iced_layershell::reexport::KeyboardInteractivity::OnDemand, - events_transparent: false, - anchor: iced_layershell::reexport::Anchor::empty(), - margin: Default::default(), +fn layer_shell_settings() -> window::settings::LayerShellSettings { + window::settings::LayerShellSettings { + layer: Some(window::settings::Layer::Top), + anchor: None, + output: None, exclusive_zone: Some(0), - size: Some((WINDOW_WIDTH as u32, WINDOW_HEIGHT as u32)), - use_last_output: false, - namespace: None, + margin: None, + input_region: None, + keyboard_interactivity: Some(window::settings::KeyboardInteractivity::OnDemand), + namespace: Some("gauntlet".to_string()), } } - -fn open_main_window_non_wayland( - minimized: bool, - window_position_file: Option<&PathBuf>, -) -> (window::Id, Task) { - let position = window_position_file - .map(|window_position_file| fs::read_to_string(window_position_file).ok()) - .flatten() - .map(|data| { - if let Some((x, y)) = data.split_once(":") { - match (x.parse(), y.parse()) { - (Ok(x), Ok(y)) => Some(Position::Specific(Point::new(x, y))), - _ => None, - } - } else { - None - } - }) - .unwrap_or(None) - .unwrap_or(Position::Centered); - - let (main_window_id, open_task) = window::open(window_settings(!minimized, position)); - - ( - main_window_id, - Task::batch([ - open_task.discard(), - window::gain_focus(main_window_id), - window::set_level(main_window_id, Level::AlwaysOnTop), - ]), - ) -} - -#[cfg(target_os = "linux")] -fn open_main_window_wayland(id: window::Id) -> (window::Id, Task) { - let settings = layer_shell_settings(); - - ( - id, - Task::done(WindowActionMsg::LayerShell( - layer_shell::LayerShellAppMsg::NewLayerShell { id, settings }, - )), - ) -} From bced4daedd661dabaa09c7d37213d7bee8b05a6d Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 3 Jul 2025 18:59:24 +0200 Subject: [PATCH 47/91] Cli `gauntlet open` now hides window when pressed while window is open, matching behavior of global shortcut --- rust/client/src/ui/mod.rs | 2 +- rust/client/src/ui/server.rs | 6 +++--- rust/client/src/ui/windows/mod.rs | 2 +- rust/common/src/rpc/frontend_api.rs | 2 +- rust/server/src/plugins/mod.rs | 6 ------ 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index b7bc0ed..a8cc18a 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -297,7 +297,7 @@ pub fn run(minimized: bool, scenario_runner_data: Option) { .subscription(subscription) .theme(|state, _| state.theme.clone()) .run() - .expect("Unable to start scenario application"); + .expect("Unable to start application"); } fn new(minimized: bool, #[allow(unused)] scenario_runner_data: Option) -> (AppModel, Task) { diff --git a/rust/client/src/ui/server.rs b/rust/client/src/ui/server.rs index 355dfbb..93618d6 100644 --- a/rust/client/src/ui/server.rs +++ b/rust/client/src/ui/server.rs @@ -115,10 +115,10 @@ async fn request_loop( AppMsg::ClearInlineView { plugin_id } } - FrontendApiRequestData::ShowWindow {} => { - responder.respond(Ok(FrontendApiResponseData::ShowWindow { data: () })); + FrontendApiRequestData::ToggleWindow {} => { + responder.respond(Ok(FrontendApiResponseData::ToggleWindow { data: () })); - AppMsg::WindowAction(WindowActionMsg::ShowWindow) + AppMsg::WindowAction(WindowActionMsg::ToggleWindow) } FrontendApiRequestData::HideWindow {} => { responder.respond(Ok(FrontendApiResponseData::HideWindow { data: () })); diff --git a/rust/client/src/ui/windows/mod.rs b/rust/client/src/ui/windows/mod.rs index 3332cd5..f87dc15 100644 --- a/rust/client/src/ui/windows/mod.rs +++ b/rust/client/src/ui/windows/mod.rs @@ -313,7 +313,7 @@ fn window_settings(position: Position) -> window::Settings { } #[cfg(target_os = "macos")] -pub fn macos_focus_previous_app() -> Task { +pub fn macos_focus_previous_app() { unsafe { // when closing NSPanel current active application doesn't automatically become key window // is there a proper way? without doing this manually diff --git a/rust/common/src/rpc/frontend_api.rs b/rust/common/src/rpc/frontend_api.rs index 4dc3e3d..cdc0f81 100644 --- a/rust/common/src/rpc/frontend_api.rs +++ b/rust/common/src/rpc/frontend_api.rs @@ -30,7 +30,7 @@ pub trait FrontendApi { async fn clear_inline_view(&self, plugin_id: PluginId) -> RequestResult<()>; - async fn show_window(&self) -> RequestResult<()>; + async fn toggle_window(&self) -> RequestResult<()>; async fn hide_window(&self) -> RequestResult<()>; diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 2c894a8..319c7b8 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -185,12 +185,6 @@ impl ApplicationManager { result } - pub async fn show_window(&self) -> anyhow::Result<()> { - self.frontend_api.show_window().await?; - - Ok(()) - } - pub async fn run_action( &self, plugin_id: PluginId, From 3a457450cb5a73a56b0674ba25ca5ff44db35c01 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 4 Jul 2025 19:50:55 +0200 Subject: [PATCH 48/91] Relax layer_shell requirement on linux wayland. Add config file setting to choose between normal and layer shell window --- Cargo.lock | 1 + Cargo.toml | 1 + dev_data/config/config.toml | 5 +- rust/client/Cargo.toml | 1 + rust/client/src/ui/mod.rs | 9 +- rust/client/src/ui/server.rs | 10 ++- rust/client/src/ui/wayland.rs | 37 ++++++++ rust/client/src/ui/windows/hud.rs | 4 +- rust/client/src/ui/windows/mod.rs | 13 ++- rust/common/src/model.rs | 1 + rust/plugin_runtime/Cargo.toml | 2 +- rust/server/src/plugins/config_reader.rs | 89 ------------------- rust/server/src/plugins/mod.rs | 25 +++--- rust/server/src/plugins/settings/config.rs | 33 +++++++ .../src/plugins/settings/config_reader.rs | 34 +++++++ rust/server/src/plugins/settings/mod.rs | 53 ++++++++++- 16 files changed, 202 insertions(+), 116 deletions(-) create mode 100644 rust/client/src/ui/wayland.rs delete mode 100644 rust/server/src/plugins/config_reader.rs create mode 100644 rust/server/src/plugins/settings/config.rs create mode 100644 rust/server/src/plugins/settings/config_reader.rs diff --git a/Cargo.lock b/Cargo.lock index 69b62a2..275a38c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4532,6 +4532,7 @@ dependencies = [ "once_cell", "serde", "serde_json", + "smithay-client-toolkit", "tokio", "tracing", "tray-icon", diff --git a/Cargo.toml b/Cargo.toml index 6e3f43e..a52df41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ toml = "0.8" x11rb = { version = "0.13", features = ["extra-traits"] } x11rb-async = { version = "0.13", features = ["extra-traits"] } x11rb-protocol = { version = "0.13" } +smithay-client-toolkit = { version = "0.19.2" } [dependencies] gauntlet-cli = { path = "rust/cli" } diff --git a/dev_data/config/config.toml b/dev_data/config/config.toml index 440aa77..823010c 100644 --- a/dev_data/config/config.toml +++ b/dev_data/config/config.toml @@ -1,3 +1,6 @@ #[main_window] -#close_on_unfocus = false \ No newline at end of file +#close_on_unfocus = false + +#[main_window.wayland] +#mode = "normal" diff --git a/rust/client/Cargo.toml b/rust/client/Cargo.toml index 95ac512..1fbee33 100644 --- a/rust/client/Cargo.toml +++ b/rust/client/Cargo.toml @@ -31,6 +31,7 @@ tray-icon = { version = "0.19.2", default-features = false } [target.'cfg(target_os = "linux")'.dependencies] x11rb-async.workspace = true +smithay-client-toolkit.workspace = true [target.'cfg(target_os = "macos")'.dependencies] objc2-app-kit = { version = "0.2.2", features = ["NSWorkspace"] } diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index a8cc18a..c498916 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -77,6 +77,8 @@ mod widget_container; pub mod scenario_runner; mod server; +#[cfg(target_os = "linux")] +mod wayland; mod windows; pub use theme::GauntletComplexTheme; @@ -306,7 +308,10 @@ fn new(minimized: bool, #[allow(unused)] scenario_runner_data: Option (Arc, GlobalHotKeyManager, UiSetupData, Task) { let (frontend_sender, frontend_receiver) = channel::(); let (server_grpc_sender, server_grpc_receiver) = channel::(); - let application_manager = ApplicationManager::create(frontend_sender).expect("Unable to setup application manager"); + #[cfg(target_os = "linux")] + let layer_shell_supported = layer_shell_supported(); + #[cfg(not(target_os = "linux"))] + let layer_shell_supported = false; + + let application_manager = ApplicationManager::create(frontend_sender, layer_shell_supported) + .expect("Unable to setup application manager"); let global_hotkey_manager = GlobalHotKeyManager::new().expect("Unable to setup shortcut manager"); let grpc_api = ServerGrpcApiProxy::new(server_grpc_sender); diff --git a/rust/client/src/ui/wayland.rs b/rust/client/src/ui/wayland.rs new file mode 100644 index 0000000..b78517b --- /dev/null +++ b/rust/client/src/ui/wayland.rs @@ -0,0 +1,37 @@ +use smithay_client_toolkit::reexports::client::Connection; +use smithay_client_toolkit::reexports::client::Dispatch; +use smithay_client_toolkit::reexports::client::QueueHandle; +use smithay_client_toolkit::reexports::client::globals::GlobalListContents; +use smithay_client_toolkit::reexports::client::globals::registry_queue_init; +use smithay_client_toolkit::reexports::client::protocol::wl_registry; + +struct WaylandState; + +pub fn layer_shell_supported() -> bool { + let Ok(conn) = Connection::connect_to_env() else { + return false; + }; + + let Ok((globals, _)) = registry_queue_init::(&conn) else { + return false; + }; + + globals + .contents() + .clone_list() + .iter() + .find(|global| global.interface == "zwlr_layer_shell_v1") + .is_some() +} + +impl Dispatch for WaylandState { + fn event( + _state: &mut WaylandState, + _proxy: &wl_registry::WlRegistry, + _event: wl_registry::Event, + _data: &GlobalListContents, + _conn: &Connection, + _qhandle: &QueueHandle, + ) { + } +} diff --git a/rust/client/src/ui/windows/hud.rs b/rust/client/src/ui/windows/hud.rs index e2a6306..6e9bc95 100644 --- a/rust/client/src/ui/windows/hud.rs +++ b/rust/client/src/ui/windows/hud.rs @@ -13,7 +13,7 @@ use crate::ui::windows::WindowActionMsg; const HUD_WINDOW_WIDTH: f32 = 400.0; const HUD_WINDOW_HEIGHT: f32 = 40.0; -pub fn show_hud_window(#[cfg(target_os = "linux")] wayland: bool) -> Task { +pub fn show_hud_window(#[cfg(target_os = "linux")] layer_shell: bool) -> Task { let settings = Settings { size: Size::new(HUD_WINDOW_WIDTH, HUD_WINDOW_HEIGHT), position: Position::SpecificWith(|window, screen| { @@ -34,7 +34,7 @@ pub fn show_hud_window(#[cfg(target_os = "linux")] wayland: bool) -> Task, @@ -35,6 +37,7 @@ impl WindowState { close_on_unfocus: bool, window_position_mode: WindowPositionMode, #[cfg(target_os = "linux")] wayland: bool, + #[cfg(target_os = "linux")] layer_shell: bool, ) -> WindowState { let open_position = window_position_file .as_ref() @@ -57,6 +60,8 @@ impl WindowState { main_window_id: None, focused: false, #[cfg(target_os = "linux")] + layer_shell, + #[cfg(target_os = "linux")] wayland, window_position_mode, close_on_unfocus, @@ -99,7 +104,7 @@ impl WindowState { WindowActionMsg::ShowHud { display } => { let show_hud = show_hud_window( #[cfg(target_os = "linux")] - self.wayland, + self.layer_shell, ) .map(AppMsg::WindowAction); @@ -225,7 +230,7 @@ impl WindowState { let (main_window_id, open_task) = window::open(window_settings( #[cfg(target_os = "linux")] - self.wayland, + self.layer_shell, self.open_position, )); @@ -266,7 +271,7 @@ const WINDOW_WIDTH: f32 = 750.0; const WINDOW_HEIGHT: f32 = 450.0; #[cfg(not(target_os = "macos"))] -fn window_settings(#[cfg(target_os = "linux")] wayland: bool, position: Position) -> window::Settings { +fn window_settings(#[cfg(target_os = "linux")] layer_shell: bool, position: Position) -> window::Settings { window::Settings { size: Size::new(WINDOW_WIDTH, WINDOW_HEIGHT), position, @@ -279,7 +284,7 @@ fn window_settings(#[cfg(target_os = "linux")] wayland: bool, position: Position #[cfg(target_os = "linux")] platform_specific: window::settings::PlatformSpecific { application_id: "gauntlet".to_string(), - layer_shell: if wayland { + layer_shell: if layer_shell { layer_shell_settings() } else { Default::default() diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index f2f31a1..633cd9e 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -219,6 +219,7 @@ pub struct UiSetupData { pub theme: UiTheme, pub close_on_unfocus: bool, pub window_position_mode: WindowPositionMode, + pub layer_shell: bool, } #[derive(Debug)] diff --git a/rust/plugin_runtime/Cargo.toml b/rust/plugin_runtime/Cargo.toml index bd573b8..f2f3f18 100644 --- a/rust/plugin_runtime/Cargo.toml +++ b/rust/plugin_runtime/Cargo.toml @@ -50,7 +50,7 @@ freedesktop-icons = "0.2" wayland-protocols-wlr = { version = "0.3.5", features = ["client"] } cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols.git" } wayland-client = "0.31.7" -smithay-client-toolkit = "0.19.2" +smithay-client-toolkit.workspace = true encoding = "0.2" [target.'cfg(target_os = "macos")'.dependencies] diff --git a/rust/server/src/plugins/config_reader.rs b/rust/server/src/plugins/config_reader.rs deleted file mode 100644 index 0f416df..0000000 --- a/rust/server/src/plugins/config_reader.rs +++ /dev/null @@ -1,89 +0,0 @@ -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; - -use gauntlet_common::dirs::Dirs; -use serde::Deserialize; - -pub struct ConfigReader { - dirs: Dirs, - close_on_unfocus: AtomicBool, -} - -impl ConfigReader { - pub fn new(dirs: Dirs) -> Self { - Self { - dirs, - close_on_unfocus: AtomicBool::new(true), - } - } - - pub fn reload_config(&self) -> anyhow::Result<()> { - let config = self.read_config(); - - self.close_on_unfocus.store( - config.main_window.unwrap_or_default().close_on_unfocus, - Ordering::SeqCst, - ); - - Ok(()) - } - - fn read_config(&self) -> ApplicationConfig { - let config_file = self.dirs.config_file(); - let config_content = std::fs::read_to_string(config_file); - - match config_content { - Ok(config_content) => { - toml::from_str(&config_content).unwrap_or_else(|err| { - tracing::error!("Unable to parse config, error: {:#}", err); - - ApplicationConfig::default() - }) - } - Err(_) => { - tracing::info!("No config found, using default configuration"); - - ApplicationConfig::default() - } - } - } - - pub fn close_on_unfocus(&self) -> bool { - self.close_on_unfocus.load(Ordering::SeqCst) - } -} - -#[derive(Debug, Deserialize, Default)] -#[serde(deny_unknown_fields)] -pub struct ApplicationConfig { - main_window: Option, - // #[serde(default)] - // configuration_mode: ConfigurationModeConfig, - // #[serde(default)] - // plugins: Vec, -} - -#[derive(Debug, Deserialize)] -pub struct ApplicationConfigWindow { - close_on_unfocus: bool, -} - -impl Default for ApplicationConfigWindow { - fn default() -> Self { - Self { close_on_unfocus: true } - } -} - -// #[derive(Debug, Deserialize)] -// struct PluginEntryConfig { -// id: String, -// } - -// #[derive(Deserialize, Debug, Default)] -// enum ConfigurationModeConfig { -// #[serde(rename = "config")] -// Config, -// #[default] -// #[serde(rename = "config_and_state")] -// ConfigAndState -// } diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 319c7b8..e27f782 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -42,7 +42,6 @@ use include_dir::include_dir; use itertools::Itertools; use crate::plugins::clipboard::Clipboard; -use crate::plugins::config_reader::ConfigReader; use crate::plugins::data_db_repository::DataDbRepository; use crate::plugins::data_db_repository::DbPluginClipboardPermissions; use crate::plugins::data_db_repository::DbPluginEntrypointType; @@ -71,7 +70,6 @@ use crate::search::SearchIndex; mod binary_data_gatherer; mod clipboard; -mod config_reader; mod data_db_repository; mod download_status; pub(super) mod frecency; @@ -89,7 +87,6 @@ static BUNDLED_PLUGINS: [(&str, Dir); 1] = [( )]; pub struct ApplicationManager { - config_reader: ConfigReader, search_index: SearchIndex, command_broadcaster: tokio::sync::broadcast::Sender, db_repository: DataDbRepository, @@ -105,16 +102,21 @@ pub struct ApplicationManager { impl ApplicationManager { pub fn create( frontend_sender: RequestSender, + layer_shell_supported: bool, ) -> anyhow::Result { let frontend_api = FrontendApiProxy::new(frontend_sender); let dirs = Dirs::new(); let db_repository = DataDbRepository::new(dirs.clone())?; let plugin_downloader = PluginLoader::new(db_repository.clone()); - let config_reader = ConfigReader::new(dirs.clone()); let icon_cache = IconCache::new(dirs.clone()); let run_status_holder = RunStatusHolder::new(); let clipboard = Clipboard::new()?; - let settings = Settings::new(dirs.clone(), db_repository.clone(), frontend_api.clone())?; + let settings = Settings::new( + dirs.clone(), + db_repository.clone(), + frontend_api.clone(), + layer_shell_supported, + )?; let search_index = SearchIndex::create_index(frontend_api.clone(), settings.clone())?; let (command_broadcaster, _) = tokio::sync::broadcast::channel::(100); @@ -122,7 +124,6 @@ impl ApplicationManager { icon_cache.clear_all_icon_cache_dir()?; let application_manager = Self { - config_reader, search_index, command_broadcaster, db_repository, @@ -157,13 +158,15 @@ impl ApplicationManager { let window_position_file = self.dirs.window_position(); let theme = self.settings.effective_theme()?; let window_position_mode = self.settings.window_position_mode_setting()?; - let close_on_unfocus = self.config_reader.close_on_unfocus(); + let close_on_unfocus = self.settings.close_on_unfocus(); + let layer_shell = self.settings.layer_shell(); Ok(UiSetupData { window_position_file: Some(window_position_file), theme, close_on_unfocus, window_position_mode, + layer_shell, }) } @@ -639,17 +642,9 @@ impl ApplicationManager { Ok(()) } - pub fn reload_config(&self) -> anyhow::Result<()> { - self.config_reader.reload_config()?; - - Ok(()) - } - pub fn reload_all_plugins(&self) -> anyhow::Result<()> { tracing::info!("Reloading all plugins"); - self.reload_config()?; - for plugin in self.db_repository.list_plugins()? { let plugin_id = PluginId::from_string(plugin.id); let running = self.run_status_holder.is_plugin_running(&plugin_id); diff --git a/rust/server/src/plugins/settings/config.rs b/rust/server/src/plugins/settings/config.rs new file mode 100644 index 0000000..943e5d6 --- /dev/null +++ b/rust/server/src/plugins/settings/config.rs @@ -0,0 +1,33 @@ +use serde::Deserialize; + +#[derive(Deserialize, Debug, Default)] +#[serde(deny_unknown_fields)] +pub struct ApplicationConfig { + pub main_window: Option, +} + +#[derive(Deserialize, Debug, Default)] +pub struct ApplicationWindowConfig { + pub close_on_unfocus: Option, + pub wayland: Option, +} + +#[derive(Deserialize, Debug, Default)] +pub struct ApplicationWindowWaylandConfig { + pub mode: Option, +} + +#[derive(Deserialize, Debug)] +pub enum WaylandLayerShellConfig { + #[serde(rename = "prefer_layer_shell")] + PreferLayerShell, + #[serde(rename = "normal")] + Normal, + #[serde(rename = "layer_shell")] // errors if not available + LayerShell, +} + +pub struct EffectiveConfig { + pub close_on_unfocus: bool, + pub layer_shell: bool, +} diff --git a/rust/server/src/plugins/settings/config_reader.rs b/rust/server/src/plugins/settings/config_reader.rs new file mode 100644 index 0000000..13e97a4 --- /dev/null +++ b/rust/server/src/plugins/settings/config_reader.rs @@ -0,0 +1,34 @@ +use gauntlet_common::dirs::Dirs; + +use crate::plugins::settings::config::ApplicationConfig; + +#[derive(Clone)] +pub struct ConfigReader { + dirs: Dirs, +} + +impl ConfigReader { + pub fn new(dirs: Dirs) -> Self { + Self { dirs } + } + + pub fn read_config(&self) -> ApplicationConfig { + let config_file = self.dirs.config_file(); + let config_content = std::fs::read_to_string(config_file); + + match config_content { + Ok(config_content) => { + toml::from_str(&config_content).unwrap_or_else(|err| { + tracing::error!("Unable to parse config, error: {:#}", err); + + ApplicationConfig::default() + }) + } + Err(_) => { + tracing::info!("No config found, using default configuration"); + + ApplicationConfig::default() + } + } + } +} diff --git a/rust/server/src/plugins/settings/mod.rs b/rust/server/src/plugins/settings/mod.rs index f5dd11d..a377e47 100644 --- a/rust/server/src/plugins/settings/mod.rs +++ b/rust/server/src/plugins/settings/mod.rs @@ -1,3 +1,5 @@ +mod config; +pub mod config_reader; pub mod global_shortcut; use std::collections::HashMap; @@ -20,6 +22,10 @@ use crate::plugins::data_db_repository::DataDbRepository; use crate::plugins::data_db_repository::DbSettingsEntrypointSearchAliasData; use crate::plugins::data_db_repository::DbTheme; use crate::plugins::data_db_repository::DbWindowPositionMode; +use crate::plugins::settings::config::ApplicationConfig; +use crate::plugins::settings::config::EffectiveConfig; +use crate::plugins::settings::config::WaylandLayerShellConfig; +use crate::plugins::settings::config_reader::ConfigReader; use crate::plugins::settings::global_shortcut::GlobalShortcutAction; use crate::plugins::settings::global_shortcut::GlobalShortcutPressedEvent; use crate::plugins::settings::global_shortcut::GlobalShortcutSettings; @@ -31,17 +37,28 @@ pub struct Settings { dirs: Dirs, repository: DataDbRepository, frontend_api: FrontendApiProxy, + config: Arc, global_hotkey_settings: GlobalShortcutSettings, themes: Arc, } impl Settings { - pub fn new(dirs: Dirs, repository: DataDbRepository, frontend_api: FrontendApiProxy) -> anyhow::Result { + pub fn new( + dirs: Dirs, + repository: DataDbRepository, + frontend_api: FrontendApiProxy, + layer_shell_supported: bool, + ) -> anyhow::Result { + let config_reader = ConfigReader::new(dirs.clone()); + + let config = config_reader.read_config(); + Ok(Self { - dirs, + dirs: dirs.clone(), repository: repository.clone(), frontend_api, global_hotkey_settings: GlobalShortcutSettings::new(repository)?, + config: Arc::new(effective_config(config, layer_shell_supported)), themes: Arc::new(BundledThemes::new()?), }) } @@ -271,4 +288,36 @@ impl Settings { Mode::Default => self.themes.macos_dark_theme.clone(), } } + + pub fn close_on_unfocus(&self) -> bool { + self.config.close_on_unfocus + } + + pub fn layer_shell(&self) -> bool { + self.config.layer_shell + } +} + +fn effective_config(config: ApplicationConfig, layer_shell_supported: bool) -> EffectiveConfig { + let main_window = config.main_window.unwrap_or_default(); + + let close_on_unfocus = main_window.close_on_unfocus.unwrap_or(true); + + let layer_shell = main_window + .wayland + .unwrap_or_default() + .mode + .map(|mode| { + match mode { + WaylandLayerShellConfig::PreferLayerShell => layer_shell_supported, + WaylandLayerShellConfig::Normal => false, + WaylandLayerShellConfig::LayerShell => true, + } + }) + .unwrap_or(layer_shell_supported); + + EffectiveConfig { + close_on_unfocus, + layer_shell, + } } From a8287ede600e92089ffa48222ba3d881e589de4a Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 6 Jul 2025 12:02:33 +0200 Subject: [PATCH 49/91] Update iced and winit fork --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 28 ++++++++++++++-------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 275a38c..901c636 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5761,7 +5761,7 @@ dependencies = [ [[package]] name = "iced" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "iced_core", "iced_debug", @@ -5777,7 +5777,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "bitflags 2.9.1", "bytes", @@ -5795,7 +5795,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "iced_core", "iced_futures", @@ -5826,7 +5826,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "futures", "iced_core", @@ -5840,7 +5840,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "bitflags 2.9.1", "bytemuck", @@ -5860,7 +5860,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "iced_graphics", "iced_runtime", @@ -5869,7 +5869,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -5881,7 +5881,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "bytes", "iced_core", @@ -5894,7 +5894,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "bytemuck", "cosmic-text", @@ -5911,7 +5911,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "bitflags 2.9.1", "bytemuck", @@ -5931,7 +5931,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "iced_renderer", "iced_runtime", @@ -5946,7 +5946,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#f249e3e1ff3a544de441e1665dab81030485f84c" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" dependencies = [ "iced_debug", "iced_program", @@ -13291,7 +13291,7 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winit" version = "0.30.99" -source = "git+https://github.com/project-gauntlet/winit.git?rev=e9023f4f0434ba86dae50b1e92e53e565bef82ab#e9023f4f0434ba86dae50b1e92e53e565bef82ab" +source = "git+https://github.com/project-gauntlet/winit.git?rev=3f32c4232443a9572e6c96b5fcbb1fd9ad636f89#3f32c4232443a9572e6c96b5fcbb1fd9ad636f89" dependencies = [ "ahash 0.8.12", "android-activity", diff --git a/Cargo.toml b/Cargo.toml index a52df41..724ba55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,17 +85,17 @@ lto = "thin" strip = true #[patch.crates-io] -#iced_fonts = { path = "../../../RustroverProjects/iced_fonts_fork" } -#iced = { path = "../../../RustroverProjects/iced-fork" } -#iced_debug = { path = "../../../RustroverProjects/iced-fork/debug" } -#iced_program = { path = "../../../RustroverProjects/iced-fork/program" } -#iced_core = { path = "../../../RustroverProjects/iced-fork/core" } -#iced_futures = { path = "../../../RustroverProjects/iced-fork/futures" } -#iced_graphics = { path = "../../../RustroverProjects/iced-fork/graphics" } -#iced_renderer = { path = "../../../RustroverProjects/iced-fork/renderer" } -#iced_runtime = { path = "../../../RustroverProjects/iced-fork/runtime" } -#iced_tiny_skia = { path = "../../../RustroverProjects/iced-fork/tiny_skia" } -#iced_wgpu = { path = "../../../RustroverProjects/iced-fork/wgpu" } -#iced_widget = { path = "../../../RustroverProjects/iced-fork/widget" } -#iced_winit = { path = "../../../RustroverProjects/iced-fork/winit" } -#winit = { path = "../../../RustroverProjects/winit" } +#iced_fonts = { path = "../iced_fonts" } +#iced = { path = "../iced" } +#iced_debug = { path = "../iced/debug" } +#iced_program = { path = "../iced/program" } +#iced_core = { path = "../iced/core" } +#iced_futures = { path = "../iced/futures" } +#iced_graphics = { path = "../iced/graphics" } +#iced_renderer = { path = "../iced/renderer" } +#iced_runtime = { path = "../iced/runtime" } +#iced_tiny_skia = { path = "../iced/tiny_skia" } +#iced_wgpu = { path = "../iced/wgpu" } +#iced_widget = { path = "../iced/widget" } +#iced_winit = { path = "../iced/winit" } +#winit = { path = "../winit" } From 08c5bb2c496f996b535b0ae098cf81c3b07e7bd4 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 6 Jul 2025 13:13:42 +0200 Subject: [PATCH 50/91] Fix sys tray open main and settings windows actions causing deadlock --- rust/client/src/ui/mod.rs | 6 +++--- rust/client/src/ui/server.rs | 2 +- rust/client/src/ui/sys_tray.rs | 22 ++++++++++++++++++---- rust/server/src/plugins/mod.rs | 7 ++++++- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index c498916..a7a6faf 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -345,10 +345,10 @@ fn new(minimized: bool, #[allow(unused)] scenario_runner_data: Option { - self.application_manager.handle_open_settings_window(); + self.application_manager.open_settings_window(); Task::none() } diff --git a/rust/client/src/ui/server.rs b/rust/client/src/ui/server.rs index 637c1ed..0336510 100644 --- a/rust/client/src/ui/server.rs +++ b/rust/client/src/ui/server.rs @@ -243,7 +243,7 @@ pub fn handle_server_message( ServerGrpcApiRequestData::ShowSettingsWindow {} => { responder.respond(Ok(ServerGrpcApiResponseData::ShowSettingsWindow { data: () })); - state.application_manager.handle_open_settings_window(); + state.application_manager.open_settings_window(); Task::none() } diff --git a/rust/client/src/ui/sys_tray.rs b/rust/client/src/ui/sys_tray.rs index 0bd2567..6b328de 100644 --- a/rust/client/src/ui/sys_tray.rs +++ b/rust/client/src/ui/sys_tray.rs @@ -1,6 +1,9 @@ +use std::sync::Arc; use image::ImageFormat; +use tokio::runtime::Handle; +use gauntlet_server::plugins::ApplicationManager; -pub fn create_tray() -> tray_icon::TrayIcon { +pub fn create_tray(application_manager: Arc) -> tray_icon::TrayIcon { use tray_icon::TrayIconBuilder; use tray_icon::menu::AboutMetadataBuilder; use tray_icon::menu::Menu; @@ -11,10 +14,21 @@ pub fn create_tray() -> tray_icon::TrayIcon { use tray_icon::menu::accelerator::CMD_OR_CTRL; use tray_icon::menu::accelerator::Code; - MenuEvent::set_event_handler(Some(|event: MenuEvent| { + let handle = Handle::current(); + + MenuEvent::set_event_handler(Some(move |event: MenuEvent| { match event.id().as_ref() { - "GAUNTLET_OPEN_MAIN_WINDOW" => gauntlet_common::cli::open_window(), - "GAUNTLET_OPEN_SETTING_WINDOW" => gauntlet_common::cli::open_settings_window(), + "GAUNTLET_OPEN_MAIN_WINDOW" => { + handle.spawn({ + let application_manager = application_manager.clone(); + async move { + application_manager.open_window().await; + } + }); + }, + "GAUNTLET_OPEN_SETTING_WINDOW" => { + application_manager.open_settings_window(); + }, _ => {} } })); diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index e27f782..9150bea 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -801,7 +801,12 @@ impl ApplicationManager { } } - pub fn handle_open_settings_window(&self) { + pub async fn open_window(&self) { + self.frontend_api.toggle_window().await + .expect("failed to toggle window"); + } + + pub fn open_settings_window(&self) { let current_exe = std::env::current_exe().expect("unable to get current_exe"); std::process::Command::new(current_exe) From 1c23514aacbd0e221236bc96380aceade914b8ca Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 6 Jul 2025 13:15:14 +0200 Subject: [PATCH 51/91] Fix formatting --- rust/client/src/ui/sys_tray.rs | 7 ++++--- rust/server/src/plugins/mod.rs | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/rust/client/src/ui/sys_tray.rs b/rust/client/src/ui/sys_tray.rs index 6b328de..d762de7 100644 --- a/rust/client/src/ui/sys_tray.rs +++ b/rust/client/src/ui/sys_tray.rs @@ -1,7 +1,8 @@ use std::sync::Arc; + +use gauntlet_server::plugins::ApplicationManager; use image::ImageFormat; use tokio::runtime::Handle; -use gauntlet_server::plugins::ApplicationManager; pub fn create_tray(application_manager: Arc) -> tray_icon::TrayIcon { use tray_icon::TrayIconBuilder; @@ -25,10 +26,10 @@ pub fn create_tray(application_manager: Arc) -> tray_icon::T application_manager.open_window().await; } }); - }, + } "GAUNTLET_OPEN_SETTING_WINDOW" => { application_manager.open_settings_window(); - }, + } _ => {} } })); diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 9150bea..1823ad0 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -802,7 +802,9 @@ impl ApplicationManager { } pub async fn open_window(&self) { - self.frontend_api.toggle_window().await + self.frontend_api + .toggle_window() + .await .expect("failed to toggle window"); } From 94c89846492688b2d1d5bc2528e1fede4041a6b1 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:26:00 +0200 Subject: [PATCH 52/91] Disable global shortcuts by default on wayland, add config option to enable if legacy x11 api is supported --- dev_data/config/config.toml | 6 +- rust/client/src/ui/mod.rs | 9 ++- rust/client/src/ui/server.rs | 59 ++++++++++++++++--- rust/common/src/model.rs | 1 + rust/common/src/rpc/backend_api.rs | 2 + rust/common/src/rpc/server_grpc_api.rs | 2 + rust/management_client/src/ui.rs | 15 ++++- rust/management_client/src/views/general.rs | 34 +++++++++-- rust/management_client/src/views/plugins.rs | 3 + .../src/views/plugins/table.rs | 45 ++++++++------ rust/server/src/plugins/mod.rs | 12 ++-- rust/server/src/plugins/settings/config.rs | 26 +++++--- rust/server/src/plugins/settings/mod.rs | 49 ++++++++------- rust/server/src/rpc.rs | 4 ++ 14 files changed, 196 insertions(+), 71 deletions(-) diff --git a/dev_data/config/config.toml b/dev_data/config/config.toml index 823010c..6deeabd 100644 --- a/dev_data/config/config.toml +++ b/dev_data/config/config.toml @@ -2,5 +2,7 @@ #[main_window] #close_on_unfocus = false -#[main_window.wayland] -#mode = "normal" +[wayland] +#main_window_surface = "xdg_shell" +global_shortcuts_api = "legacy_x11_api" +#global_shortcuts_api = "none" diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index a7a6faf..727feee 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -108,7 +108,7 @@ use crate::ui::windows::x11_focus::x11_linux_focus_change_subscription; pub struct AppModel { // logic application_manager: Arc, - global_hotkey_manager: GlobalHotKeyManager, + global_hotkey_manager: Option, #[cfg(any(target_os = "macos", target_os = "windows"))] _tray_icon: tray_icon::TrayIcon, theme: GauntletComplexTheme, @@ -303,13 +303,16 @@ pub fn run(minimized: bool, scenario_runner_data: Option) { } fn new(minimized: bool, #[allow(unused)] scenario_runner_data: Option) -> (AppModel, Task) { - let (application_manager, global_hotkey_manager, setup_data, setup_task) = server::setup(); - #[cfg(target_os = "linux")] let wayland = std::env::var("WAYLAND_DISPLAY") .or_else(|_| std::env::var("WAYLAND_SOCKET")) .is_ok(); + let (application_manager, global_hotkey_manager, setup_data, setup_task) = server::setup( + #[cfg(target_os = "linux")] + wayland, + ); + #[cfg(target_os = "linux")] let layer_shell = wayland && setup_data.layer_shell; diff --git a/rust/client/src/ui/server.rs b/rust/client/src/ui/server.rs index 0336510..50da1d1 100644 --- a/rust/client/src/ui/server.rs +++ b/rust/client/src/ui/server.rs @@ -2,6 +2,7 @@ use std::ops::Deref; use std::sync::Arc; use std::sync::Mutex; +use anyhow::anyhow; use gauntlet_common::model::UiSetupData; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; @@ -25,7 +26,14 @@ use crate::ui::AppMsg; use crate::ui::wayland::layer_shell_supported; use crate::ui::windows::WindowActionMsg; -pub fn setup() -> (Arc, GlobalHotKeyManager, UiSetupData, Task) { +pub fn setup( + #[cfg(target_os = "linux")] wayland: bool, +) -> ( + Arc, + Option, + UiSetupData, + Task, +) { let (frontend_sender, frontend_receiver) = channel::(); let (server_grpc_sender, server_grpc_receiver) = channel::(); @@ -36,16 +44,30 @@ pub fn setup() -> (Arc, GlobalHotKeyManager, UiSetupData, Ta let application_manager = ApplicationManager::create(frontend_sender, layer_shell_supported) .expect("Unable to setup application manager"); - let global_hotkey_manager = GlobalHotKeyManager::new().expect("Unable to setup shortcut manager"); - let grpc_api = ServerGrpcApiProxy::new(server_grpc_sender); + let grpc_api = ServerGrpcApiProxy::new(server_grpc_sender); let frontend_receiver = Arc::new(TokioRwLock::new(frontend_receiver)); let server_grpc_receiver = Arc::new(TokioRwLock::new(server_grpc_receiver)); let application_manager = Arc::new(application_manager); - let setup_data = application_manager - .setup(&global_hotkey_manager) - .expect("Unable to setup"); + let setup_data = application_manager.config().expect("Unable to setup"); + + #[cfg(target_os = "linux")] + let enable_global_hotkey_manager = !wayland || setup_data.wayland_use_legacy_x11_api; + #[cfg(not(target_os = "linux"))] + let enable_global_hotkey_manager = true; + + let global_hotkey_manager = if enable_global_hotkey_manager { + let global_hotkey_manager = GlobalHotKeyManager::new().expect("Unable to setup shortcut manager"); + + application_manager + .setup_global_shortcuts(&global_hotkey_manager) + .expect("Unable to setup"); + + Some(global_hotkey_manager) + } else { + None + }; let mut tasks = vec![]; @@ -313,9 +335,14 @@ pub fn handle_server_message( Task::none() } ServerGrpcApiRequestData::SetGlobalShortcut { shortcut } => { + let Some(global_hotkey_manager) = &state.global_hotkey_manager else { + responder.respond(Err(anyhow!("Global hotkey manager is disabled"))); + return Task::none(); + }; + let result = state .application_manager - .set_global_shortcut(&state.global_hotkey_manager, shortcut.clone()); + .set_global_shortcut(global_hotkey_manager, shortcut.clone()); responder.respond(Ok(ServerGrpcApiResponseData::SetGlobalShortcut { data: result })); @@ -336,10 +363,15 @@ pub fn handle_server_message( entrypoint_id, shortcut, } => { + let Some(global_hotkey_manager) = &state.global_hotkey_manager else { + responder.respond(Err(anyhow!("Global hotkey manager is disabled"))); + return Task::none(); + }; + let result = state .application_manager .set_global_entrypoint_shortcut( - &state.global_hotkey_manager, + global_hotkey_manager, plugin_id.clone(), entrypoint_id.clone(), shortcut.clone(), @@ -479,6 +511,17 @@ pub fn handle_server_message( responder.respond(result); + Task::none() + } + ServerGrpcApiRequestData::WaylandGlobalShortcutsEnabled {} => { + let result = state.application_manager.config().map(|data| { + ServerGrpcApiResponseData::WaylandGlobalShortcutsEnabled { + data: data.wayland_use_legacy_x11_api, + } + }); + + responder.respond(result); + Task::none() } } diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index 633cd9e..b24d9f5 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -220,6 +220,7 @@ pub struct UiSetupData { pub close_on_unfocus: bool, pub window_position_mode: WindowPositionMode, pub layer_shell: bool, + pub wayland_use_legacy_x11_api: bool, } #[derive(Debug)] diff --git a/rust/common/src/rpc/backend_api.rs b/rust/common/src/rpc/backend_api.rs index ad0a99f..788fe2e 100644 --- a/rust/common/src/rpc/backend_api.rs +++ b/rust/common/src/rpc/backend_api.rs @@ -45,6 +45,8 @@ pub trait BackendForToolsApi { #[boundary_gen(bincode, grpc)] #[tonic::async_trait] pub trait BackendForSettingsApi { + async fn wayland_global_shortcuts_enabled(&self) -> RequestResult; + async fn plugins(&self) -> RequestResult>; async fn set_plugin_state(&self, plugin_id: PluginId, enabled: bool) -> RequestResult<()>; diff --git a/rust/common/src/rpc/server_grpc_api.rs b/rust/common/src/rpc/server_grpc_api.rs index 182927c..df1941b 100644 --- a/rust/common/src/rpc/server_grpc_api.rs +++ b/rust/common/src/rpc/server_grpc_api.rs @@ -85,4 +85,6 @@ pub trait ServerGrpcApi { async fn download_status(&self) -> RequestResult>; async fn remove_plugin(&self, plugin_id: PluginId) -> RequestResult<()>; + + async fn wayland_global_shortcuts_enabled(&self) -> RequestResult; } diff --git a/rust/management_client/src/ui.rs b/rust/management_client/src/ui.rs index 492ad1f..2948fcc 100644 --- a/rust/management_client/src/ui.rs +++ b/rust/management_client/src/ui.rs @@ -120,6 +120,14 @@ fn new() -> (ManagementAppModel, Task) { .inspect_err(|err| tracing::error!("Unable to connect to server: {:?}", err)) .ok(); + let wayland = if cfg!(target_os = "linux") { + std::env::var("WAYLAND_DISPLAY") + .or_else(|_| std::env::var("WAYLAND_SOCKET")) + .is_ok() + } else { + false + }; + ( ManagementAppModel { backend_api: backend_api.clone(), @@ -139,7 +147,7 @@ fn new() -> (ManagementAppModel, Task) { None => None, } }) - .then(|init_data| { + .then(move |init_data| { match init_data { None => Task::done(ManagementAppMsg::General(ManagementAppGeneralMsgIn::Noop)), Some(init) => { @@ -151,9 +159,11 @@ fn new() -> (ManagementAppModel, Task) { window_position_mode: init.window_position_mode, shortcut: init.global_shortcut, shortcut_error: init.global_shortcut_error, + global_shortcuts_unsupported: wayland && !init.wayland_global_shortcuts_enabled, })), Task::done(ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::InitSetting { global_entrypoint_shortcuts: init.global_entrypoint_shortcuts, + show_global_shortcuts: !wayland || init.wayland_global_shortcuts_enabled, })), ]) } @@ -172,6 +182,7 @@ struct InitSettingsData { theme: SettingsTheme, window_position_mode: WindowPositionMode, global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, + wayland_global_shortcuts_enabled: bool, } async fn init_data(backend_api: impl BackendForSettingsApi) -> RequestResult { @@ -181,6 +192,7 @@ async fn init_data(backend_api: impl BackendForSettingsApi) -> RequestResult RequestResult, shortcut_error: Option, + global_shortcuts_unsupported: bool, }, Noop, } @@ -66,6 +70,7 @@ impl ManagementAppGeneralState { shortcut: None, error: None, }, + global_shortcuts_unsupported: false, } } @@ -107,6 +112,7 @@ impl ManagementAppGeneralState { window_position_mode, shortcut, shortcut_error, + global_shortcuts_unsupported, } => { self.theme = theme; self.window_position_mode = window_position_mode; @@ -114,6 +120,7 @@ impl ManagementAppGeneralState { shortcut, error: shortcut_error, }; + self.global_shortcuts_unsupported = global_shortcuts_unsupported; Task::none() } @@ -164,12 +171,27 @@ impl ManagementAppGeneralState { } pub fn view(&self) -> Element { - let global_shortcut_selector = shortcut_selector( - &self.current_shortcut, - move |shortcut| ManagementAppGeneralMsgIn::ShortcutCaptured(shortcut), - ContainerStyle::Box, - false, - ); + let global_shortcut_selector: Element<_> = if self.global_shortcuts_unsupported { + let text = text("Not supported").font(Font { + style: Style::Italic, + ..Font::DEFAULT + }); + + container(text) + .width(Length::Fill) + .height(Length::Fill) + .center(Length::Fill) + .class(ContainerStyle::Box) + .into() + } else { + shortcut_selector( + &self.current_shortcut, + move |shortcut| ManagementAppGeneralMsgIn::ShortcutCaptured(shortcut), + ContainerStyle::Box, + false, + ) + .into() + }; let global_shortcut_field: Element<_> = container(global_shortcut_selector) .width(Length::Fill) diff --git a/rust/management_client/src/views/plugins.rs b/rust/management_client/src/views/plugins.rs index c5cc8ef..3132904 100644 --- a/rust/management_client/src/views/plugins.rs +++ b/rust/management_client/src/views/plugins.rs @@ -48,6 +48,7 @@ mod table; pub enum ManagementAppPluginMsgIn { InitSetting { global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, + show_global_shortcuts: bool, }, PluginTableMsg(PluginTableMsgIn), PluginPreferenceMsg(PluginPreferencesMsg), @@ -135,8 +136,10 @@ impl ManagementAppPluginsState { match message { ManagementAppPluginMsgIn::InitSetting { global_entrypoint_shortcuts, + show_global_shortcuts, } => { self.global_entrypoint_shortcuts = global_entrypoint_shortcuts; + self.table_state.show_global_shortcuts = show_global_shortcuts; Task::none() } diff --git a/rust/management_client/src/views/plugins/table.rs b/rust/management_client/src/views/plugins/table.rs index 4da26df..b9ca674 100644 --- a/rust/management_client/src/views/plugins/table.rs +++ b/rust/management_client/src/views/plugins/table.rs @@ -74,11 +74,15 @@ pub enum PluginTableMsgOut { pub struct PluginTableState { rows: Vec, + pub show_global_shortcuts: bool, } impl PluginTableState { pub fn new() -> Self { - Self { rows: vec![] } + Self { + rows: vec![], + show_global_shortcuts: false, + } } pub fn update(&mut self, message: PluginTableMsgIn) -> Task { @@ -229,7 +233,11 @@ impl PluginTableState { style, name_cell(row_item), type_cell(row_item), - shortcut_cell(row_item), + if self.show_global_shortcuts { + Some(shortcut_cell(row_item)) + } else { + None + }, alias_cell(row_item), enable_cell(row_item), ) @@ -240,8 +248,12 @@ impl PluginTableState { ContainerStyle::TableEvenRow, header_cell("Name", true), header_cell("Type", false), + if self.show_global_shortcuts { + Some(header_cell("Shortcut", false)) + } else { + None + }, header_cell("Alias", false), - header_cell("Shortcut", false), header_cell("Enabled", false), ); @@ -290,21 +302,20 @@ enum Row { fn table_row<'a>( style: ContainerStyle, - first_cell: Element<'a, PluginTableMsgIn>, - second_cell: Element<'a, PluginTableMsgIn>, - third_cell: Element<'a, PluginTableMsgIn>, - fourth_cell: Element<'a, PluginTableMsgIn>, - fifth_cell: Element<'a, PluginTableMsgIn>, + name_cell: Element<'a, PluginTableMsgIn>, + type_cell: Element<'a, PluginTableMsgIn>, + shortcut_cell: Option>, + alias_cell: Element<'a, PluginTableMsgIn>, + enable_cell: Element<'a, PluginTableMsgIn>, ) -> Element<'a, PluginTableMsgIn> { - container(row([ - container(first_cell).width(Length::FillPortion(38)).into(), - container(second_cell).width(Length::FillPortion(12)).into(), - container(third_cell).width(Length::FillPortion(24)).into(), - container(fourth_cell).width(Length::FillPortion(15)).into(), - container(fifth_cell).width(Length::FillPortion(10)).into(), - ])) - .class(style) - .into() + let content = row([]) + .push(container(name_cell).width(Length::FillPortion(38))) + .push(container(type_cell).width(Length::FillPortion(12))) + .push_maybe(shortcut_cell.map(|item| container(item).width(Length::FillPortion(24)))) + .push(container(alias_cell).width(Length::FillPortion(15))) + .push(container(enable_cell).width(Length::FillPortion(10))); + + container(content).class(style).into() } fn header_cell<'a>(value: &str, first: bool) -> Element<'a, PluginTableMsgIn> { diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 1823ad0..97e73ce 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -152,14 +152,17 @@ impl ApplicationManager { Ok(application_manager) } - pub fn setup(&self, global_hotkey_manager: &GlobalHotKeyManager) -> anyhow::Result { - self.settings.setup(global_hotkey_manager)?; + pub fn setup_global_shortcuts(&self, global_hotkey_manager: &GlobalHotKeyManager) -> anyhow::Result<()> { + Ok(self.settings.setup_global_shortcuts(global_hotkey_manager)?) + } + pub fn config(&self) -> anyhow::Result { let window_position_file = self.dirs.window_position(); let theme = self.settings.effective_theme()?; let window_position_mode = self.settings.window_position_mode_setting()?; - let close_on_unfocus = self.settings.close_on_unfocus(); - let layer_shell = self.settings.layer_shell(); + let close_on_unfocus = self.settings.config().close_on_unfocus; + let layer_shell = self.settings.config().layer_shell; + let wayland_use_legacy_x11_api = self.settings.config().wayland_use_legacy_x11_api; Ok(UiSetupData { window_position_file: Some(window_position_file), @@ -167,6 +170,7 @@ impl ApplicationManager { close_on_unfocus, window_position_mode, layer_shell, + wayland_use_legacy_x11_api, }) } diff --git a/rust/server/src/plugins/settings/config.rs b/rust/server/src/plugins/settings/config.rs index 943e5d6..f6c30db 100644 --- a/rust/server/src/plugins/settings/config.rs +++ b/rust/server/src/plugins/settings/config.rs @@ -4,30 +4,40 @@ use serde::Deserialize; #[serde(deny_unknown_fields)] pub struct ApplicationConfig { pub main_window: Option, + pub wayland: Option, } #[derive(Deserialize, Debug, Default)] pub struct ApplicationWindowConfig { pub close_on_unfocus: Option, - pub wayland: Option, } #[derive(Deserialize, Debug, Default)] -pub struct ApplicationWindowWaylandConfig { - pub mode: Option, +pub struct WaylandConfig { + pub main_window_surface: Option, + pub global_shortcuts_api: Option, } #[derive(Deserialize, Debug)] -pub enum WaylandLayerShellConfig { - #[serde(rename = "prefer_layer_shell")] +pub enum WaylandMainWindowConfig { + #[serde(rename = "prefer_wlr_layer_shell")] // default PreferLayerShell, - #[serde(rename = "normal")] - Normal, - #[serde(rename = "layer_shell")] // errors if not available + #[serde(rename = "xdg_shell")] + XdgShell, + #[serde(rename = "wlr_layer_shell")] // errors if not available LayerShell, } +#[derive(Deserialize, Debug)] +pub enum WaylandGlobalShortcutConfig { + #[serde(rename = "none")] // default + None, + #[serde(rename = "legacy_x11_api")] + LegacyX11Api, +} + pub struct EffectiveConfig { pub close_on_unfocus: bool, pub layer_shell: bool, + pub wayland_use_legacy_x11_api: bool, } diff --git a/rust/server/src/plugins/settings/mod.rs b/rust/server/src/plugins/settings/mod.rs index a377e47..7e23978 100644 --- a/rust/server/src/plugins/settings/mod.rs +++ b/rust/server/src/plugins/settings/mod.rs @@ -24,7 +24,8 @@ use crate::plugins::data_db_repository::DbTheme; use crate::plugins::data_db_repository::DbWindowPositionMode; use crate::plugins::settings::config::ApplicationConfig; use crate::plugins::settings::config::EffectiveConfig; -use crate::plugins::settings::config::WaylandLayerShellConfig; +use crate::plugins::settings::config::WaylandGlobalShortcutConfig; +use crate::plugins::settings::config::WaylandMainWindowConfig; use crate::plugins::settings::config_reader::ConfigReader; use crate::plugins::settings::global_shortcut::GlobalShortcutAction; use crate::plugins::settings::global_shortcut::GlobalShortcutPressedEvent; @@ -63,7 +64,7 @@ impl Settings { }) } - pub fn setup(&self, global_hotkey_manager: &GlobalHotKeyManager) -> anyhow::Result<()> { + pub fn setup_global_shortcuts(&self, global_hotkey_manager: &GlobalHotKeyManager) -> anyhow::Result<()> { self.global_hotkey_settings.setup(global_hotkey_manager)?; Ok(()) @@ -289,35 +290,39 @@ impl Settings { } } - pub fn close_on_unfocus(&self) -> bool { - self.config.close_on_unfocus - } - - pub fn layer_shell(&self) -> bool { - self.config.layer_shell + pub fn config(&self) -> Arc { + self.config.clone() } } fn effective_config(config: ApplicationConfig, layer_shell_supported: bool) -> EffectiveConfig { - let main_window = config.main_window.unwrap_or_default(); + let window_config = config.main_window.unwrap_or_default(); + let wayland_config = config.wayland.unwrap_or_default(); - let close_on_unfocus = main_window.close_on_unfocus.unwrap_or(true); + let close_on_unfocus = window_config.close_on_unfocus.unwrap_or(true); - let layer_shell = main_window - .wayland - .unwrap_or_default() - .mode - .map(|mode| { - match mode { - WaylandLayerShellConfig::PreferLayerShell => layer_shell_supported, - WaylandLayerShellConfig::Normal => false, - WaylandLayerShellConfig::LayerShell => true, - } - }) - .unwrap_or(layer_shell_supported); + let main_window_surface = wayland_config + .main_window_surface + .unwrap_or(WaylandMainWindowConfig::PreferLayerShell); + + let layer_shell = match main_window_surface { + WaylandMainWindowConfig::PreferLayerShell => layer_shell_supported, + WaylandMainWindowConfig::XdgShell => false, + WaylandMainWindowConfig::LayerShell => true, + }; + + let main_window_surface = wayland_config + .global_shortcuts_api + .unwrap_or(WaylandGlobalShortcutConfig::None); + + let wayland_use_legacy_x11_api = match main_window_surface { + WaylandGlobalShortcutConfig::None => false, + WaylandGlobalShortcutConfig::LegacyX11Api => true, + }; EffectiveConfig { close_on_unfocus, layer_shell, + wayland_use_legacy_x11_api, } } diff --git a/rust/server/src/rpc.rs b/rust/server/src/rpc.rs index 42fed62..9b68e61 100644 --- a/rust/server/src/rpc.rs +++ b/rust/server/src/rpc.rs @@ -80,6 +80,10 @@ impl BackendForToolsApi for BackendServerImpl { #[tonic::async_trait] impl BackendForSettingsApi for BackendServerImpl { + async fn wayland_global_shortcuts_enabled(&self) -> RequestResult { + self.proxy.wayland_global_shortcuts_enabled().await + } + async fn plugins(&self) -> RequestResult> { self.proxy.plugins().await } From 6bb5f40065989bc99e8c1adca4b2bb6c9f95d747 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:37:45 +0200 Subject: [PATCH 53/91] Change action panel shortcut to Ctrl/Cmd + K, to match similar pattern in other apps --- rust/client/src/ui/mod.rs | 32 +++++++++++++++---------------- rust/client/src/ui/widget/root.rs | 6 +++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 727feee..a655e55 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -1996,7 +1996,7 @@ impl AppModel { Some(PhysicalShortcut { physical_key: PhysicalKey::Comma, modifier_shift: false, - modifier_control: cfg!(any(target_os = "linux", target_os = "windows")), + modifier_control: cfg!(not(target_os = "macos")), modifier_alt: false, modifier_meta: cfg!(target_os = "macos"), }) => { @@ -2007,10 +2007,10 @@ impl AppModel { Some(PhysicalShortcut { physical_key: PhysicalKey::KeyK, modifier_shift: false, - modifier_control: false, - modifier_alt: true, - modifier_meta: false, - }) => Task::perform(async {}, |_| AppMsg::ToggleActionPanel { keyboard: true }), + modifier_control: cfg!(not(target_os = "macos")), + modifier_alt: false, + modifier_meta: cfg!(target_os = "macos"), + }) => Task::done(AppMsg::ToggleActionPanel { keyboard: true }), Some(PhysicalShortcut { physical_key, modifier_shift, @@ -2054,9 +2054,9 @@ impl AppModel { Some(PhysicalShortcut { physical_key: PhysicalKey::KeyK, modifier_shift: false, - modifier_control: false, - modifier_alt: true, - modifier_meta: false, + modifier_control: cfg!(not(target_os = "macos")), + modifier_alt: false, + modifier_meta: cfg!(target_os = "macos"), }) => Task::perform(async {}, |_| AppMsg::ToggleActionPanel { keyboard: true }), Some(PhysicalShortcut { physical_key, @@ -2095,10 +2095,10 @@ impl AppModel { Some(PhysicalShortcut { physical_key: PhysicalKey::KeyK, modifier_shift: false, - modifier_control: false, - modifier_alt: true, - modifier_meta: false, - }) => Task::perform(async {}, |_| AppMsg::ToggleActionPanel { keyboard: true }), + modifier_control: cfg!(not(target_os = "macos")), + modifier_alt: false, + modifier_meta: cfg!(target_os = "macos"), + }) => Task::done(AppMsg::ToggleActionPanel { keyboard: true }), Some(PhysicalShortcut { physical_key, modifier_shift, @@ -2129,10 +2129,10 @@ impl AppModel { Some(PhysicalShortcut { physical_key: PhysicalKey::KeyK, modifier_shift: false, - modifier_control: false, - modifier_alt: true, - modifier_meta: false, - }) => Task::perform(async {}, |_| AppMsg::ToggleActionPanel { keyboard: true }), + modifier_control: cfg!(not(target_os = "macos")), + modifier_alt: false, + modifier_meta: cfg!(target_os = "macos"), + }) => Task::done(AppMsg::ToggleActionPanel { keyboard: true }), Some(PhysicalShortcut { physical_key, modifier_shift, diff --git a/rust/client/src/ui/widget/root.rs b/rust/client/src/ui/widget/root.rs index 933edcd..55afa91 100644 --- a/rust/client/src/ui/widget/root.rs +++ b/rust/client/src/ui/widget/root.rs @@ -306,9 +306,9 @@ pub fn render_root<'a, T: 'a + Clone>( let shortcut = render_shortcut(&PhysicalShortcut { physical_key: PhysicalKey::KeyK, modifier_shift: false, - modifier_control: false, - modifier_alt: true, - modifier_meta: false, + modifier_control: !cfg!(target_os = "macos"), + modifier_alt: false, + modifier_meta: cfg!(target_os = "macos"), }); let mut bottom_panel_content = vec![entrypoint_name]; From 5c2db4d0487b93097bbfd3785f3488427504dc23 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 7 Jul 2025 20:57:54 +0200 Subject: [PATCH 54/91] Update CHANGELOG.md --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2e4682..a821fe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,35 @@ For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://git ## [Unreleased] +### General + +- Linux Gnome Wayland support + - `zwlr_layer_shell_v1` Wayland protocol is no longer required. It is still preferred, but if not supported application falls back to regular `xdg_shell` window + - Added `wayland.main_window_surface` config option to allow customization of this behavior +- Linux Wayland LayerShell improvements + - **BREAKING CHANGE**: Changed LayerShell surface namespace from `Gauntlet` to `gauntlet` for main window, and set namespace to `gauntlet-hud` for hud window + - Migrated to yet another LayerShell implementation + - Fixes event/keystroke duplication after suspend +- Disabled global shortcuts by default on Linux Wayland + - Added `wayland.global_shortcuts_api` config option to allow usage of legacy x11 api if supported by given environment + - Global Shortcuts XDG Portal is not and will not be supported until there will be major changes to it +- Input Method Editor (IME) support for input fields +- Changed action panel shortcut from ALT + K to CTRL + K (Windows/Linux) and CMD + K (macOS), to match similar pattern in other apps +- `gauntlet open` CLI command now hides window when executed while window is open, matching behavior of global shortcut +- Lots of internal dependency updates + +### Plugins - Updated Deno to 2.3.3 +- **BREAKING CHANGE**: Remove `` component + - It caused difficulties when updating dependencies and needs a complete rework + +### Fixes +- Reduced glibc requirement from 2.38 to 2.35 +- Fixed systray open main and settings windows actions causing deadlock +- Fixed LayerShell window taking exclusive keyboard focus preventing any desktop interactions while window was open +- Fixed `useStorage` and `useCache` hooks crashing plugin runtime when closing the view +- Fixed `npm run dev` not showing full error cause (contributed by @Gabrielbdd) +- Fixed nix applications not being detected on macOS by following links inside `/Applications` (contributed by @deadbaed) ## [19] - 2025-05-11 From 771e2f95d756bd9ce02d175711fd6b76eed38a76 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+Exidex@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:01:00 +0000 Subject: [PATCH 55/91] Prepare for v20 release --- CHANGELOG.md | 2 ++ VERSION | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a821fe5..8422b89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://git ## [Unreleased] +## [20] - 2025-07-07 + ### General - Linux Gnome Wayland support diff --git a/VERSION b/VERSION index dec2bf5..2edeafb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -19 \ No newline at end of file +20 \ No newline at end of file From e643dcc4f76d9cdfe1fedd1d20db25f605afacfb Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 7 Jul 2025 22:04:14 +0200 Subject: [PATCH 56/91] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index b811762..f94b7ae 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,6 @@ https://github.com/user-attachments/assets/19964ed6-9cd9-48d4-9835-6be04de14b66 ##### Best-effort - Linux Wayland - - Gnome Wayland is not yet supported, see [#40](https://github.com/project-gauntlet/gauntlet/issues/40) - Windows - macOS Intel From 97fc6360eadf7fb2f2b742f47b393651e267441a Mon Sep 17 00:00:00 2001 From: dennkaii <70287696+dennkaii@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:00:46 -0400 Subject: [PATCH 57/91] Fix nix build (#85) --- flake.lock | 30 +++++++++++++++--------------- nix/overlay.nix | 16 ++++++++-------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/flake.lock b/flake.lock index 8e4e519..832366a 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "crane": { "locked": { - "lastModified": 1746291859, - "narHash": "sha256-DdWJLA+D5tcmrRSg5Y7tp/qWaD05ATI4Z7h22gd1h7Q=", + "lastModified": 1751562746, + "narHash": "sha256-smpugNIkmDeicNz301Ll1bD7nFOty97T79m4GUMUczA=", "owner": "ipetkov", "repo": "crane", - "rev": "dfd9a8dfd09db9aad544c4d3b6c47b12562544a5", + "rev": "aed2020fd3dc26e1e857d4107a5a67a33ab6c1fd", "type": "github" }, "original": { @@ -18,11 +18,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", "owner": "edolstra", "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", "type": "github" }, "original": { @@ -36,11 +36,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1743550720, - "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "lastModified": 1751413152, + "narHash": "sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "rev": "77826244401ea9de6e3bac47c2db46005e1f30b5", "type": "github" }, "original": { @@ -51,11 +51,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1746663147, - "narHash": "sha256-Ua0drDHawlzNqJnclTJGf87dBmaO/tn7iZ+TCkTRpRc=", + "lastModified": 1751984180, + "narHash": "sha256-LwWRsENAZJKUdD3SpLluwDmdXY9F45ZEgCb0X+xgOL0=", "owner": "nixos", "repo": "nixpkgs", - "rev": "dda3dcd3fe03e991015e9a74b22d35950f264a54", + "rev": "9807714d6944a957c2e036f84b0ff8caf9930bc0", "type": "github" }, "original": { @@ -67,11 +67,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1743296961, - "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "lastModified": 1751159883, + "narHash": "sha256-urW/Ylk9FIfvXfliA1ywh75yszAbiTEVgpPeinFyVZo=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "rev": "14a40a1d7fb9afa4739275ac642ed7301a9ba1ab", "type": "github" }, "original": { diff --git a/nix/overlay.nix b/nix/overlay.nix index be282c4..95531ad 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -6,14 +6,13 @@ flake.overlays.default = final: _: let version = "v${builtins.readFile ./../VERSION}"; # These must be updated following the instructions in ./nix/README.md when dependencies are updated or the version bumped - npmDepsHash = "sha256-AAnbT3B68U+dTqTfCA4eYFo+VQBcY4LTj98Nnq+sIes="; - RUSTY_V8_ARCHIVE = fetchRustyV8 "130.0.2" { - aarch64-darwin = "sha256-aWZ/4Q4Wttx37xOdBmTCPGP+eYGhr4CM1UkYq8pC7Qs="; - aarch64-linux = "sha256-p9+tHmKIM5wBABubHIAstpwfzO19ypPzOuaV4b6loCU="; - x86_64-darwin = "sha256-zNC0DAkMbbFM1M+t6rgKtN0QAm4ONEbCi6Sxivhf8dk="; - x86_64-linux = "sha256-ew2WZhdsHfffRQtif076AWAlFohwPo/RbmW/6D3LzkU="; + npmDepsHash = "sha256-CrQeEFpyKiYOTuNg6lZz7HwF1EEM4zV2iXse5UcKSFY="; + RUSTY_V8_ARCHIVE = fetchRustyV8 "137.2.0" { + aarch64-darwin = "sha256-fgmFCBPeD7u9qXcVjgCO1oTr1mXLrtvqy4rMftZO0iE="; + aarch64-linux = "sha256-0+2QcsjAx80Ethrulnp0nDQRphkuaDfuaW5GkIxWmb8="; + x86_64-darwin = "sha256-p+B1tpPImRJt9R+TJpuUhiX7Q8dh7emf/Hca8K18Zh8="; + x86_64-linux = "sha256-9M67FZ4TzjoiGL73B8Jtwn38lW521yCLIqyvGzYCc50="; }; - inherit (final) # Libraries @@ -38,6 +37,7 @@ protobuf wayland yq + rustPlatform ; inherit (buildPackages.npmHooks.override {inherit nodejs;}) npmConfigHook; inherit (lib) concatStringsSep getExe' makeBinPath makeLibraryPath optional; @@ -68,7 +68,7 @@ cargoArtifactsArgs = { inherit pname src version RUSTY_V8_ARCHIVE; cargoExtraArgs = "--features release"; - nativeBuildInputs = [cmake pkg-config protobuf]; + nativeBuildInputs = [cmake pkg-config protobuf rustPlatform.bindgenHook]; buildInputs = [openssl] ++ optional isLinux libxkbcommon; # OPENSSL_CONFIG_DIR didn't work for vendored dependencies OPENSSL_NO_VENDOR = true; From b7b84131464709edd58ba978780159ee4375e4a6 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 14 Jul 2025 22:06:33 +0200 Subject: [PATCH 58/91] Fix inline view being recreated each time key is pressed in search bar, causing useRef not preserving value --- js/api/src/hooks.ts | 2 ++ js/core/src/render.tsx | 22 ++++++++++------ js/react_renderer/src/renderer.ts | 4 +++ js/typings/index.d.ts | 2 +- rust/client/src/ui/client_context.rs | 20 +++++++++------ rust/client/src/ui/mod.rs | 37 +++++++++++++-------------- rust/client/src/ui/server.rs | 5 ---- rust/client/src/ui/state/mod.rs | 7 +++-- rust/client/src/ui/windows/mod.rs | 2 +- rust/common/src/rpc/frontend_api.rs | 2 -- rust/common_plugin_runtime/src/api.rs | 1 - rust/plugin_runtime/src/deno.rs | 2 -- rust/plugin_runtime/src/ui.rs | 15 ----------- rust/server/src/plugins/js.rs | 6 ----- 14 files changed, 57 insertions(+), 70 deletions(-) diff --git a/js/api/src/hooks.ts b/js/api/src/hooks.ts index e452ca4..a68d87b 100644 --- a/js/api/src/hooks.ts +++ b/js/api/src/hooks.ts @@ -171,6 +171,8 @@ function usePromiseInternal( abortable.current = undefined; } + console.error("Error happened when executing promise: ", error) + onError?.(error); } return diff --git a/js/core/src/render.tsx b/js/core/src/render.tsx index 1c2f988..2a7bb97 100644 --- a/js/core/src/render.tsx +++ b/js/core/src/render.tsx @@ -1,29 +1,35 @@ import { - clear_inline_view, fetch_action_id_for_shortcut, - op_log_debug, op_log_trace, hide_window } from "ext:core/ops"; -import { clearRenderer, render } from "ext:gauntlet/renderer.js"; +import { clearRenderer, rerender, render } from "ext:gauntlet/renderer.js"; import type { FC } from "react"; let latestRootUiWidget: UiWidget | undefined = undefined +let latestRootUiRenderLocation: RenderLocation | undefined = undefined export function renderView(entrypointId: string, entrypointName: string, View: FC) { + latestRootUiRenderLocation = "View"; latestRootUiWidget = render(entrypointId, entrypointName, "View", ); } export function renderInlineView(entrypointId: string, entrypointName: string, Handler: FC<{ text: string }>, text: string) { - latestRootUiWidget = render(entrypointId, entrypointName, "InlineView", ); - - if (latestRootUiWidget.widgetChildren.length === 0) { - op_log_debug("plugin_loop", `Inline view rendered no children, clearing inline view...`) - clear_inline_view() + switch (latestRootUiRenderLocation) { + case "InlineView": { + rerender(); + break + } + default: { + latestRootUiRenderLocation = "InlineView"; + latestRootUiWidget = render(entrypointId, entrypointName, "InlineView", ); + break + } } } export function closeView() { + latestRootUiRenderLocation = undefined; clearRenderer() } diff --git a/js/react_renderer/src/renderer.ts b/js/react_renderer/src/renderer.ts index 6e8fb7a..efae759 100644 --- a/js/react_renderer/src/renderer.ts +++ b/js/react_renderer/src/renderer.ts @@ -441,6 +441,10 @@ export function clearRenderer() { gauntletContextValue.clear() } +export function rerender(view: ReactNode) { + gauntletContextValue.rerender(view) +} + export function render(entrypointId: string, entrypointName: string, renderLocation: RenderLocation, view: ReactNode): UiWidget { const hostConfig = createHostConfig(); diff --git a/js/typings/index.d.ts b/js/typings/index.d.ts index d1de71d..c34dfa6 100644 --- a/js/typings/index.d.ts +++ b/js/typings/index.d.ts @@ -188,6 +188,7 @@ declare module "ext:gauntlet/renderer.js" { import { ReactNode } from "react"; export const render: (entrypointId: string, entrypointName: string, renderLocation: RenderLocation, component: ReactNode) => UiWidget; + export const rerender: (component: ReactNode) => void; export const clearRenderer: () => void; } @@ -235,7 +236,6 @@ declare module "ext:core/ops" { function op_inline_view_entrypoint_id(): string | null; function op_entrypoint_names(): Record; - function clear_inline_view(): void; function op_plugin_get_pending_event(): Promise; function hide_window(): void; diff --git a/rust/client/src/ui/client_context.rs b/rust/client/src/ui/client_context.rs index 5b64535..d5cfa67 100644 --- a/rust/client/src/ui/client_context.rs +++ b/rust/client/src/ui/client_context.rs @@ -98,14 +98,18 @@ impl ClientContext { ) -> AppMsg { match render_location { UiRenderLocation::InlineView => { - self.get_mut_inline_view_container(plugin_id).replace_view( - container, - data, - plugin_id, - plugin_name, - entrypoint_id, - entrypoint_name, - ) + if let Some(_) = container.content { + self.get_mut_inline_view_container(plugin_id).replace_view( + container, + data, + plugin_id, + plugin_name, + entrypoint_id, + entrypoint_name, + ) + } else { + AppMsg::ClosePluginView(plugin_id.clone()) + } } UiRenderLocation::View => { self.get_mut_view_container().replace_view( diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index a655e55..d015c67 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -209,7 +209,8 @@ pub enum AppMsg { entrypoint_id: EntrypointId, }, ShowBackendError(RequestError), - ClosePluginView, + CloseAllReactViews, + ClosePluginView(PluginId), OpenPluginView(PluginId, EntrypointId), InlineViewShortcuts { shortcuts: HashMap>, @@ -259,9 +260,6 @@ pub enum AppMsg { FocusPluginViewSearchBar { widget_id: UiWidgetId, }, - ClearInlineView { - plugin_id: PluginId, - }, SetTheme { theme: UiTheme, }, @@ -1007,12 +1005,24 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { }) } AppMsg::OpenPluginView(plugin_id, entrypoint_id) => state.open_plugin_view(plugin_id, entrypoint_id), - AppMsg::ClosePluginView => { + AppMsg::CloseAllReactViews => { if let GlobalState::PluginView { plugin_view_data, .. } = &state.global_state { - state.close_plugin_view(plugin_view_data.plugin_id.clone()) - } else { - Task::none() + let plugin_id = plugin_view_data.plugin_id.clone(); + state.application_manager.request_view_close(plugin_id); } + + for (plugin_id, _) in state.client_context.get_all_inline_view_containers() { + state.application_manager.request_view_close(plugin_id.clone()); + } + state.client_context.clear_all_inline_views(); + + Task::none() + } + AppMsg::ClosePluginView(plugin_id) => { + state.application_manager.request_view_close(plugin_id.clone()); + state.client_context.clear_inline_view(&plugin_id); + + Task::none() } AppMsg::InlineViewShortcuts { shortcuts } => { state.client_context.set_inline_view_shortcuts(shortcuts); @@ -1077,11 +1087,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } AppMsg::FocusPluginViewSearchBar { widget_id } => state.client_context.focus_search_bar(widget_id), AppMsg::WindowAction(action) => state.window.handle_action(action), - AppMsg::ClearInlineView { plugin_id } => { - state.client_context.clear_inline_view(&plugin_id); - - Task::none() - } AppMsg::SetTheme { theme } => { state.theme = GauntletComplexTheme::new(theme); @@ -1796,12 +1801,6 @@ impl AppModel { Task::done(msg) } - fn close_plugin_view(&self, plugin_id: PluginId) -> Task { - self.application_manager.request_view_close(plugin_id); - - Task::none() - } - fn run_command(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> Task { self.application_manager.run_command(plugin_id, entrypoint_id); diff --git a/rust/client/src/ui/server.rs b/rust/client/src/ui/server.rs index 50da1d1..91ae342 100644 --- a/rust/client/src/ui/server.rs +++ b/rust/client/src/ui/server.rs @@ -140,11 +140,6 @@ async fn request_loop( data: images, } } - FrontendApiRequestData::ClearInlineView { plugin_id } => { - responder.respond(Ok(FrontendApiResponseData::ClearInlineView { data: () })); - - AppMsg::ClearInlineView { plugin_id } - } FrontendApiRequestData::ToggleWindow {} => { responder.respond(Ok(FrontendApiResponseData::ToggleWindow { data: () })); diff --git a/rust/client/src/ui/state/mod.rs b/rust/client/src/ui/state/mod.rs index 365382b..ea11bc8 100644 --- a/rust/client/src/ui/state/mod.rs +++ b/rust/client/src/ui/state/mod.rs @@ -314,11 +314,14 @@ impl Focus for GlobalState { if *top_level_view { if *close_window_on_esc { Task::batch([ - Task::done(AppMsg::ClosePluginView), + Task::done(AppMsg::ClosePluginView(plugin_id.clone())), Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)), ]) } else { - Task::batch([Task::done(AppMsg::ClosePluginView), GlobalState::initial(self)]) + Task::batch([ + Task::done(AppMsg::ClosePluginView(plugin_id.clone())), + GlobalState::initial(self), + ]) } } else { let plugin_id = plugin_id.clone(); diff --git a/rust/client/src/ui/windows/mod.rs b/rust/client/src/ui/windows/mod.rs index dd91063..6821ec5 100644 --- a/rust/client/src/ui/windows/mod.rs +++ b/rust/client/src/ui/windows/mod.rs @@ -211,7 +211,7 @@ impl WindowState { commands.push(Task::done(AppMsg::WindowAction(WindowActionMsg::SetMainWindowId(None)))); if reset_state { - commands.push(Task::done(AppMsg::ClosePluginView)); + commands.push(Task::done(AppMsg::CloseAllReactViews)); commands.push(Task::done(AppMsg::ResetWindowState)); } diff --git a/rust/common/src/rpc/frontend_api.rs b/rust/common/src/rpc/frontend_api.rs index cdc0f81..908b242 100644 --- a/rust/common/src/rpc/frontend_api.rs +++ b/rust/common/src/rpc/frontend_api.rs @@ -28,8 +28,6 @@ pub trait FrontendApi { data: HashMap>, ) -> RequestResult<()>; - async fn clear_inline_view(&self, plugin_id: PluginId) -> RequestResult<()>; - async fn toggle_window(&self) -> RequestResult<()>; async fn hide_window(&self) -> RequestResult<()>; diff --git a/rust/common_plugin_runtime/src/api.rs b/rust/common_plugin_runtime/src/api.rs index 5345450..33376c0 100644 --- a/rust/common_plugin_runtime/src/api.rs +++ b/rust/common_plugin_runtime/src/api.rs @@ -63,5 +63,4 @@ pub trait BackendForPluginRuntimeApi { plugin_preferences_required: bool, entrypoint_preferences_required: bool, ) -> RequestResult<()>; - async fn ui_clear_inline_view(&self) -> RequestResult<()>; } diff --git a/rust/plugin_runtime/src/deno.rs b/rust/plugin_runtime/src/deno.rs index 21643bb..8607f88 100644 --- a/rust/plugin_runtime/src/deno.rs +++ b/rust/plugin_runtime/src/deno.rs @@ -73,7 +73,6 @@ use crate::preferences::get_entrypoint_preferences; use crate::preferences::get_plugin_preferences; use crate::preferences::plugin_preferences_required; use crate::search::reload_search_index; -use crate::ui::clear_inline_view; use crate::ui::fetch_action_id_for_shortcut; use crate::ui::hide_window; use crate::ui::op_component_model; @@ -273,7 +272,6 @@ deno_core::extension!( op_inline_view_entrypoint_id, op_entrypoint_names, show_plugin_error_view, - clear_inline_view, show_preferences_required_view, op_component_model, fetch_action_id_for_shortcut, diff --git a/rust/plugin_runtime/src/ui.rs b/rust/plugin_runtime/src/ui.rs index a92fd42..c805c49 100644 --- a/rust/plugin_runtime/src/ui.rs +++ b/rust/plugin_runtime/src/ui.rs @@ -70,21 +70,6 @@ pub fn show_preferences_required_view( Ok(()) } -#[op2(fast)] -pub fn clear_inline_view(state: Rc>) -> Result<(), GauntletJsError> { - let api = { - let state = state.borrow(); - - let api = state.borrow::().clone(); - - api - }; - - tokio::spawn(async move { api.ui_clear_inline_view().await }); - - Ok(()) -} - #[op2] #[string] pub fn op_inline_view_entrypoint_id(state: Rc>) -> Option { diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 22dbb38..7989161 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -1047,12 +1047,6 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { Ok(()) } - - async fn ui_clear_inline_view(&self) -> RequestResult<()> { - self.frontend_api.clear_inline_view(self.plugin_id.clone()).await?; - - Ok(()) - } } fn preferences_to_js( From 21f8ab6d0972f092392210bdb3718f040a58da9b Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Tue, 15 Jul 2025 19:48:02 +0200 Subject: [PATCH 59/91] Fix some screenshots from screenshot generator being blank --- rust/client/src/ui/scenario_runner.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/client/src/ui/scenario_runner.rs b/rust/client/src/ui/scenario_runner.rs index 667c5fe..7ee267a 100644 --- a/rust/client/src/ui/scenario_runner.rs +++ b/rust/client/src/ui/scenario_runner.rs @@ -165,6 +165,7 @@ pub fn run_scenario(data: ScenarioRunnerData, theme: UiTheme) -> Task { chain = chain .chain(wait_for(1500)) .chain(Task::done(do_screenshot)) + .chain(wait_for(100)) .chain(Task::done(close_window)) .chain(wait_for(500)) } From 3cdd16cfb3bd2024c458719153ba14736c8f7c2f Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 17 Jul 2025 08:10:05 +0200 Subject: [PATCH 60/91] Fix panic if state of the widget under specific id changed type --- rust/client/src/ui/widget/data.rs | 6 +++--- rust/client/src/ui/widget/data_mut.rs | 2 +- rust/client/src/ui/widget_container.rs | 6 +++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/rust/client/src/ui/widget/data.rs b/rust/client/src/ui/widget/data.rs index cec4f2c..a0640a1 100644 --- a/rust/client/src/ui/widget/data.rs +++ b/rust/client/src/ui/widget/data.rs @@ -71,7 +71,7 @@ impl<'b> ComponentWidgets<'b> { match state { ComponentWidgetState::Checkbox(state) => state, - _ => panic!("TextFieldState expected, {:?} found", state), + _ => panic!("CheckboxState expected, {:?} found", state), } } @@ -83,7 +83,7 @@ impl<'b> ComponentWidgets<'b> { match state { ComponentWidgetState::Select(state) => state, - _ => panic!("TextFieldState expected, {:?} found", state), + _ => panic!("SelectState expected, {:?} found", state), } } @@ -95,7 +95,7 @@ impl<'b> ComponentWidgets<'b> { match state { ComponentWidgetState::Root(state) => state, - _ => panic!("TextFieldState expected, {:?} found", state), + _ => panic!("RootState expected, {:?} found", state), } } } diff --git a/rust/client/src/ui/widget/data_mut.rs b/rust/client/src/ui/widget/data_mut.rs index 416b9fa..ad9ee38 100644 --- a/rust/client/src/ui/widget/data_mut.rs +++ b/rust/client/src/ui/widget/data_mut.rs @@ -75,7 +75,7 @@ impl<'b> ComponentWidgetsMut<'b> { match state { ComponentWidgetState::Root(state) => state, - _ => panic!("TextFieldState expected, {:?} found", state), + _ => panic!("RootState expected, {:?} found", state), } } } diff --git a/rust/client/src/ui/widget_container.rs b/rust/client/src/ui/widget_container.rs index 17a8e29..9a2fcdf 100644 --- a/rust/client/src/ui/widget_container.rs +++ b/rust/client/src/ui/widget_container.rs @@ -80,7 +80,11 @@ impl PluginWidgetContainer { for (key, value) in old_state.into_iter() { match self.state.entry(key) { Entry::Occupied(mut entry) => { - entry.insert(value); + // copy over old value, but only if type of the widget didn't change + // if it did change, the widget state is reset + if mem::discriminant(entry.get()) == mem::discriminant(&value) { + entry.insert(value); + } } Entry::Vacant(_) => {} } From 729b4f695fa9c0a2a87144a00973b14d6e35a58d Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 18 Jul 2025 17:44:51 +0200 Subject: [PATCH 61/91] Clean up couple of things --- rust/client/src/ui/widget_container.rs | 2 +- rust/common/build.rs | 181 +------------------------ rust/common/src/model.rs | 16 --- rust/plugin_runtime/src/ui.rs | 4 +- 4 files changed, 9 insertions(+), 194 deletions(-) diff --git a/rust/client/src/ui/widget_container.rs b/rust/client/src/ui/widget_container.rs index 9a2fcdf..3c1f3b4 100644 --- a/rust/client/src/ui/widget_container.rs +++ b/rust/client/src/ui/widget_container.rs @@ -98,7 +98,7 @@ impl PluginWidgetContainer { self.root_widget = Some(container); if first_open { - ComponentWidgets::new(&mut self.root_widget, &mut self.state, &self.data).first_open() + ComponentWidgets::new(&self.root_widget, &self.state, &self.data).first_open() } else { AppMsg::Noop } diff --git a/rust/common/build.rs b/rust/common/build.rs index 7017eec..88e7ef8 100644 --- a/rust/common/build.rs +++ b/rust/common/build.rs @@ -449,179 +449,10 @@ fn component_model_generator() -> Result<(), Box> { output.push_str(&format!(" }}\n")); output.push_str(&format!("}}\n")); } - - { - output.push_str(&format!("impl Serialize for {}WidgetContent {{\n", name)); - output.push_str(&format!( - " fn serialize(&self, serializer: S) -> Result\n" - )); - output.push_str(&format!(" where\n")); - output.push_str(&format!(" S: Serializer\n")); - output.push_str(&format!(" {{\n")); - - { - output.push_str(" #[derive(Debug, Serialize)]\n"); - output.push_str(" #[serde(tag = \"__type__\")]\n"); - output.push_str(&format!(" enum {}WidgetMembersRef<'a> {{\n", name)); - - for (_, prop_union_component_refs) in &prop_union_component_refs { - for prop_union_component_ref in prop_union_component_refs { - output.push_str(&format!( - " #[serde(rename = \"gauntlet:{}\")]\n", - prop_union_component_ref.component_internal_name - )); - output.push_str(&format!( - " {}(&'a {}Widget),\n", - prop_union_component_ref.component_name, - prop_union_component_ref.component_name - )); - } - } - - for component_ref in &component_refs { - output.push_str(&format!( - " #[serde(rename = \"gauntlet:{}\")]\n", - component_ref.component_internal_name - )); - output.push_str(&format!( - " {}(&'a {}Widget),\n", - component_ref.component_name, component_ref.component_name - )); - } - - if has_text { - output - .push_str(&format!(" #[serde(rename = \"gauntlet:text_part\")]\n")); - output.push_str(&format!(" Text {{\n")); - output.push_str(&format!(" value: &'a String\n")); - output.push_str(&format!(" }},\n")); - } - - output.push_str(" }\n"); - } - - output.push_str(&format!( - " let mut members = Vec::<{}WidgetMembersRef>::new();\n", - name - )); - output.push_str("\n"); - - for (prop_name, prop_union_component_refs) in &prop_union_component_refs { - output.push_str(&format!(" for item in &self.{} {{\n", prop_name)); - output.push_str(&format!(" match item {{\n")); - - for (index, prop_union_component_ref) in prop_union_component_refs.iter().enumerate() { - output.push_str(&format!( - " {}{}::_{}(widget) => {{\n", - name, - prop_name.to_case(Case::Pascal), - index - )); - output.push_str(&format!( - " members.push({}WidgetMembersRef::{}(widget));\n", - name, prop_union_component_ref.component_name - )); - output.push_str(&format!(" }}\n")); - } - - output.push_str(&format!(" }}\n")); - output.push_str(&format!(" }}\n")); - } - - for (prop_name, prop_other_component_refs) in &prop_other_component_refs { - match prop_other_component_refs.arity { - Arity::ZeroOrOne => { - output.push_str(&format!( - " if let Some({}) = &self.{} {{\n", - prop_name, prop_name - )); - output.push_str(&format!( - " members.push({}WidgetMembersRef::{}({}))\n", - name, prop_other_component_refs.component_name, prop_name - )); - output.push_str(&format!(" }}\n")); - } - Arity::One => { - output.push_str(&format!( - " members.push({}WidgetMembersRef::{}(&self.{}))\n", - name, prop_other_component_refs.component_name, prop_name - )); - } - Arity::ZeroOrMore => { - todo!() - } - } - } - - for per_type_component_ref in &per_type_component_refs { - match per_type_component_ref.arity { - Arity::ZeroOrOne => { - output.push_str(&format!( - " if let Some(item) = &self.{} {{\n", - per_type_component_ref.component_internal_name - )); - output.push_str(&format!( - " members.push({}WidgetMembersRef::{}(item))\n", - name, per_type_component_ref.component_name - )); - output.push_str(&format!(" }}\n")); - } - Arity::One => { - output.push_str(&format!( - " members.push({}WidgetMembersRef::{}(&self.{}));\n", - name, - per_type_component_ref.component_name, - per_type_component_ref.component_internal_name - )); - } - Arity::ZeroOrMore => { - todo!() - } - } - } - - if !unique_ordered_component_refs.is_empty() { - output.push_str(&format!(" for member in &self.ordered_members {{\n")); - output.push_str(&format!(" match member {{\n")); - - for ordered_component_ref in &unique_ordered_component_refs { - output.push_str(&format!( - " {}WidgetOrderedMembers::{}(widget) => {{\n", - name, ordered_component_ref.component_name - )); - output.push_str(&format!( - " members.push({}WidgetMembersRef::{}(widget))\n", - name, ordered_component_ref.component_name - )); - output.push_str(&format!(" }}\n")); - } - - output.push_str(&format!(" }}\n")); - output.push_str(&format!(" }}\n")); - } - - if has_text { - output.push_str(&format!(" for value in &self.text {{\n")); - output.push_str(&format!( - " members.push({}WidgetMembersRef::Text {{ value }});\n", - name - )); - output.push_str(&format!(" }}\n")); - } - - output.push_str("\n"); - output.push_str(&format!( - " Vec::<{}WidgetMembersRef>::serialize(&members, serializer)\n", - name - )); - - output.push_str(&format!(" }}\n")); - output.push_str(&format!("}}\n")); - } } } - output.push_str("#[derive(Debug, Serialize, Deserialize, Encode, Decode)]\n"); + output.push_str("#[derive(Debug, Deserialize, Encode, Decode)]\n"); output.push_str(&format!("pub struct {}Widget {{\n", name)); output.push_str(" #[serde(rename = \"__id__\")]\n"); output.push_str(" pub __id__: UiWidgetId,\n"); @@ -690,7 +521,7 @@ fn component_model_generator() -> Result<(), Box> { output.push_str("\n"); } SharedType::Object { items } => { - output.push_str("#[derive(Debug, Serialize, Deserialize, Encode, Decode)]\n"); + output.push_str("#[derive(Debug, Deserialize, Encode, Decode)]\n"); output.push_str(&format!("pub struct {} {{\n", type_name)); for (property_name, property_type) in items { @@ -708,7 +539,7 @@ fn component_model_generator() -> Result<(), Box> { output.push_str("\n"); } SharedType::Union { items } => { - output.push_str("#[derive(Debug, Serialize, Deserialize, Encode, Decode)]\n"); + output.push_str("#[derive(Debug, Deserialize, Encode, Decode)]\n"); output.push_str("#[serde(untagged)]\n"); output.push_str(&format!("pub enum {} {{\n", type_name)); @@ -729,7 +560,7 @@ fn component_model_generator() -> Result<(), Box> { } } - output.push_str("#[derive(Debug, Serialize, Deserialize, Encode, Decode)]\n"); + output.push_str("#[derive(Debug, Deserialize, Encode, Decode)]\n"); output.push_str("#[serde(tag = \"__type__\")]\n"); output.push_str("pub enum RootWidgetMembers {\n"); @@ -746,9 +577,9 @@ fn component_model_generator() -> Result<(), Box> { output.push_str("}\n"); - output.push_str("#[derive(Debug, Serialize, Deserialize, Encode, Decode)]\n"); + output.push_str("#[derive(Debug, Deserialize, Encode, Decode)]\n"); output.push_str("pub struct RootWidget {\n"); - output.push_str(" #[serde(default, deserialize_with = \"array_to_option\", serialize_with = \"option_to_array\")]\n"); + output.push_str(" #[serde(default, deserialize_with = \"array_to_option\")]\n"); output.push_str(" pub content: Option\n"); output.push_str("}\n"); } diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index b24d9f5..083b82c 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -12,7 +12,6 @@ use gix_url::Url; use serde::Deserialize; use serde::Deserializer; use serde::Serialize; -use serde::Serializer; use serde::de::Error; #[derive(Debug, Clone, PartialEq, Eq, Hash, Encode, Decode)] @@ -234,21 +233,6 @@ pub enum KeyboardEventOrigin { PluginView, } -fn option_to_array(value: &Option, serializer: S) -> Result -where - V: Serialize, - S: Serializer, -{ - let value = match value { - None => vec![], - Some(value) => vec![value], - }; - - let res = Vec::<&V>::serialize(&value, serializer)?; - - Ok(res) -} - fn array_to_option<'de, D, V>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, diff --git a/rust/plugin_runtime/src/ui.rs b/rust/plugin_runtime/src/ui.rs index c805c49..1c2d495 100644 --- a/rust/plugin_runtime/src/ui.rs +++ b/rust/plugin_runtime/src/ui.rs @@ -94,11 +94,11 @@ pub fn op_react_replace_view<'a>( top_level_view: bool, #[string] entrypoint_id: &str, #[string] entrypoint_name: &str, - #[serde] container: serde_v8::Value<'a>, + container: v8::Local, ) -> Result<(), GauntletJsError> { tracing::trace!(target = "renderer_rs", "Calling op_react_replace_view..."); - let mut deserializer = serde_v8::Deserializer::new(scope, container.v8_value, None); + let mut deserializer = serde_v8::Deserializer::new(scope, container, None); let container = RootWidget::deserialize(&mut deserializer) .map_err(|err| anyhow!("Unable to deserialize root widget: {:#}", err))?; From 242f5a2eeadb61824886c7c591a5f9abcb1a3782 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 20 Jul 2025 19:00:05 +0200 Subject: [PATCH 62/91] Rewrite react component parser into procmacros, better error reporting --- Cargo.lock | 9 +- js/react_renderer/src/renderer.ts | 30 +- rust/common/Cargo.toml | 9 +- rust/common/build.rs | 630 ------------- rust/common/src/model.rs | 23 +- rust/plugin_runtime/Cargo.toml | 1 + rust/plugin_runtime/src/lib.rs | 1 + .../src/model_deserialization.rs | 238 +++++ rust/plugin_runtime/src/ui.rs | 10 +- rust/utils_macros/Cargo.toml | 3 + rust/utils_macros/src/lib.rs | 12 + .../src/widget_deserialization_gen.rs | 835 ++++++++++++++++++ rust/utils_macros/src/widget_model_gen.rs | 470 ++++++++++ 13 files changed, 1584 insertions(+), 687 deletions(-) create mode 100644 rust/plugin_runtime/src/model_deserialization.rs create mode 100644 rust/utils_macros/src/widget_deserialization_gen.rs create mode 100644 rust/utils_macros/src/widget_model_gen.rs diff --git a/Cargo.lock b/Cargo.lock index 901c636..4e7e3e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4547,18 +4547,15 @@ dependencies = [ "base64 0.22.1", "bincode 2.0.1", "bytes", - "convert_case 0.6.0", "directories", - "gauntlet-component-model", "gauntlet-utils", "gauntlet-utils-macros", "gix-url", - "indexmap 2.9.0", - "itertools 0.13.0", "libc", "prost", "serde", "serde_json", + "strum 0.27.1", "tokio", "tonic", "tonic-build", @@ -4646,6 +4643,7 @@ dependencies = [ "gauntlet-common-plugin-runtime", "gauntlet-component-model", "gauntlet-utils", + "gauntlet-utils-macros", "icns", "image", "indexmap 2.9.0", @@ -4748,6 +4746,9 @@ name = "gauntlet-utils-macros" version = "0.1.0" dependencies = [ "convert_case 0.8.0", + "gauntlet-component-model", + "indexmap 2.9.0", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.103", diff --git a/js/react_renderer/src/renderer.ts b/js/react_renderer/src/renderer.ts index efae759..5a380ed 100644 --- a/js/react_renderer/src/renderer.ts +++ b/js/react_renderer/src/renderer.ts @@ -380,23 +380,21 @@ export const createHostConfig = (): HostConfig< }); -function convertComponents(widget: UiWidget): any { - const component: any = { - __id__: widget.widgetId, - __type__: widget.widgetType, - ...widget.widgetProperties - } +function convertComponents(widget: UiWidget): any { + const widgetProperties = Object.fromEntries( + Object.entries(widget.widgetProperties) + .filter(([_, value]) => typeof value !== "function") + ); - for (const [name, value] of Object.entries(component)) { - if (typeof value === "function") { - delete component[name] - } - } - - component.content = widget.widgetChildren + const widgetChildren = widget.widgetChildren .map(child => convertComponents(child)) - return component + return { + widgetId: widget.widgetId, + widgetType: widget.widgetType, + widgetProperties: widgetProperties, + widgetChildren: widgetChildren, + } } function shallowDiff(oldObj: Record, newObj: Record): string[] | null { @@ -418,7 +416,7 @@ const createTracedHostConfig = (hostConfig: any) => new Proxy(hostConfig, { return function _noop(...args: any[]) { console.log('MethodTrace Stub:', propKey, ...args.map(function (arg) { - return Deno.inspect(arg, {depth: 1}); + return Deno.inspect(arg, { depth: 1 }); })); } } @@ -426,7 +424,7 @@ const createTracedHostConfig = (hostConfig: any) => new Proxy(hostConfig, { if (typeof f === 'function') { return function _traced(this: any, ...args: any[]) { console.log('MethodTrace:', propKey, ...args.map(function (arg) { - return Deno.inspect(arg, {depth: 1}); + return Deno.inspect(arg, { depth: 1 }); })); return f.apply(this, args); diff --git a/rust/common/Cargo.toml b/rust/common/Cargo.toml index 1afbea7..0caf22f 100644 --- a/rust/common/Cargo.toml +++ b/rust/common/Cargo.toml @@ -22,22 +22,15 @@ bytes.workspace = true gix-url = { version = "0.28.1" } base64 = "0.22" directories = "5.0" +strum = { version = "0.27", features = ["derive"] } [target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] libc = "0.2" [build-dependencies] -# workspaces -gauntlet-component-model.workspace = true - # shared -itertools.workspace = true -indexmap.workspace = true tonic-build.workspace = true -# other -convert_case = "0.6.0" - [features] release = [] scenario_runner = [] diff --git a/rust/common/build.rs b/rust/common/build.rs index 88e7ef8..eb49479 100644 --- a/rust/common/build.rs +++ b/rust/common/build.rs @@ -1,637 +1,7 @@ -use std::env; -use std::fs::File; -use std::io::Write; -use std::ops::Deref; -use std::path::Path; - -use convert_case::Case; -use convert_case::Casing; -use gauntlet_component_model::Arity; -use gauntlet_component_model::Children; -use gauntlet_component_model::Component; -use gauntlet_component_model::ComponentName; -use gauntlet_component_model::ComponentRef; -use gauntlet_component_model::Property; -use gauntlet_component_model::PropertyKind; -use gauntlet_component_model::PropertyType; -use gauntlet_component_model::SharedType; -use gauntlet_component_model::create_component_model; -use indexmap::IndexMap; -use itertools::Itertools; - fn main() -> Result<(), Box> { tonic_build::configure() .protoc_arg("--experimental_allow_proto3_optional") .compile_protos(&["./../../schema/backend.proto"], &["./../../schema/"])?; - component_model_generator()?; - Ok(()) } - -fn component_model_generator() -> Result<(), Box> { - let out_dir = env::var("OUT_DIR")?; - let dest_path = Path::new(&out_dir).join("components.rs"); - - let mut output = String::new(); - - let components = create_component_model(); - - for component in &components { - match component { - Component::Standard { - name, props, children, .. - } => { - let props_has_content = props - .iter() - .any(|prop| matches!(prop.property_type.kind(), PropertyKind::Component)); - - let children_has_content = match children { - Children::Members { - ordered_members, - per_type_members, - .. - } - | Children::StringOrMembers { - ordered_members, - per_type_members, - .. - } => !ordered_members.is_empty() || !per_type_members.is_empty(), - _ => false, - }; - - let has_text = matches!(children, Children::StringOrMembers { .. } | Children::String { .. }); - - let has_content = children_has_content || props_has_content || has_text; - - let default = IndexMap::new(); - - let (ordered_members, per_type_members) = match children { - Children::Members { - ordered_members, - per_type_members, - .. - } - | Children::StringOrMembers { - ordered_members, - per_type_members, - .. - } => (ordered_members, per_type_members), - _ => (&default, &default), - }; - - if !ordered_members.is_empty() { - output.push_str("#[derive(Debug, Encode, Decode)]\n"); - output.push_str(&format!("pub enum {}WidgetOrderedMembers {{\n", name)); - - let unique_component_refs = ordered_members - .iter() - .map(|(_member_name, component_ref)| component_ref) - .unique_by(|component_ref| component_ref.component_name.clone()) - .collect::>(); - - for component_ref in &unique_component_refs { - output.push_str(&format!( - " {}({}Widget),\n", - component_ref.component_name, component_ref.component_name - )); - } - - output.push_str("}\n"); - } - - if has_content { - { - output.push_str("#[derive(Debug, Encode, Decode)]\n"); - output.push_str(&format!("pub struct {}WidgetContent {{\n", name)); - - for prop in props { - if matches!(prop.property_type.kind(), PropertyKind::Component) { - let is_union = match &prop.property_type { - PropertyType::Union { .. } => true, - PropertyType::Array { item } => matches!(item.as_ref(), PropertyType::Union { .. }), - _ => false, - }; - - if is_union { - output.push_str(&format!( - " pub {}: {},\n", - prop.name.to_case(Case::Snake), - generate_required_type( - &prop.property_type, - Some(format!("{}{}", name, &prop.name.to_case(Case::Pascal))) - ) - )); - } else { - output.push_str(&format!( - " pub {}: {},\n", - prop.name.to_case(Case::Snake), - generate_type(&prop, name) - )); - } - } - } - - for (member_name, component_ref) in per_type_members { - match component_ref.arity { - Arity::ZeroOrOne => { - output.push_str(&format!( - " pub {}: Option<{}Widget>,\n", - member_name.to_case(Case::Snake), - component_ref.component_name - )); - } - Arity::One => { - output.push_str(&format!( - " pub {}: {}Widget,\n", - member_name.to_case(Case::Snake), - component_ref.component_name - )); - } - Arity::ZeroOrMore => { - todo!() - } - } - } - - if !ordered_members.is_empty() { - output.push_str(&format!( - " pub ordered_members: Vec<{}WidgetOrderedMembers>,\n", - name - )); - } - - if has_text { - output.push_str(" pub text: Vec,\n"); - } - - output.push_str("}\n"); - } - - { - let unique_ordered_component_refs = ordered_members - .iter() - .map(|(_member_name, component_ref)| component_ref) - .unique_by(|component_ref| component_ref.component_name.clone()) - .collect::>(); - - let per_type_component_refs = per_type_members - .iter() - .map(|(_member_name, component_ref)| component_ref) - .collect::>(); - - let mut prop_union_component_refs = IndexMap::new(); - let mut prop_other_component_refs = IndexMap::new(); - - for prop in props { - let is_union = match &prop.property_type { - PropertyType::Union { .. } => true, - PropertyType::Array { item } => matches!(item.as_ref(), PropertyType::Union { .. }), - _ => false, - }; - - let prop_name = prop.name.to_case(Case::Snake); - - if is_union { - fn all_component_refs(property_type: &PropertyType) -> Vec<&ComponentRef> { - match property_type { - PropertyType::String => vec![], - PropertyType::Number => vec![], - PropertyType::Boolean => vec![], - PropertyType::Component { reference } => vec![reference], - PropertyType::Function { .. } => vec![], - PropertyType::SharedTypeRef { .. } => vec![], - PropertyType::Union { items } => { - items.iter().flat_map(|prop| all_component_refs(prop)).collect() - } - PropertyType::Array { item } => all_component_refs(item), - } - } - - prop_union_component_refs.insert(prop_name, all_component_refs(&prop.property_type)); - } else { - match &prop.property_type { - PropertyType::Component { reference } => { - prop_other_component_refs.insert(prop_name, reference); - } - PropertyType::Array { item, .. } => { - match item.as_ref() { - PropertyType::Component { reference } => { - prop_other_component_refs.insert(prop_name, reference); - } - _ => {} - } - } - _ => {} - } - } - } - - let mut component_refs = vec![]; - component_refs.extend(unique_ordered_component_refs.clone()); - component_refs.extend(per_type_component_refs.clone()); - component_refs.extend(prop_other_component_refs.values()); - - { - output.push_str(&format!("impl<'de> Deserialize<'de> for {}WidgetContent {{\n", name)); - output.push_str(&format!( - " fn deserialize(deserializer: D) -> Result\n" - )); - output.push_str(&format!(" where\n")); - output.push_str(&format!(" D: Deserializer<'de>,\n")); - output.push_str(&format!(" {{\n")); - - { - output.push_str(" #[derive(Debug, Deserialize)]\n"); - output.push_str(" #[serde(tag = \"__type__\")]\n"); - output.push_str(&format!(" enum {}WidgetMembersOwned {{\n", name)); - - for (_, prop_union_component_refs) in &prop_union_component_refs { - for prop_union_component_ref in prop_union_component_refs { - output.push_str(&format!( - " #[serde(rename = \"gauntlet:{}\")]\n", - prop_union_component_ref.component_internal_name - )); - output.push_str(&format!( - " {}({}Widget),\n", - prop_union_component_ref.component_name, - prop_union_component_ref.component_name - )); - } - } - - for component_ref in &component_refs { - output.push_str(&format!( - " #[serde(rename = \"gauntlet:{}\")]\n", - component_ref.component_internal_name - )); - output.push_str(&format!( - " {}({}Widget),\n", - component_ref.component_name, component_ref.component_name - )); - } - - if has_text { - output - .push_str(&format!(" #[serde(rename = \"gauntlet:text_part\")]\n")); - output.push_str(&format!(" Text {{\n")); - output.push_str(&format!(" value: String\n")); - output.push_str(&format!(" }},\n")); - } - - output.push_str(" }\n"); - } - - output.push_str(&format!( - " let mut members = Vec::<{}WidgetMembersOwned>::deserialize(deserializer)?;\n", - name - )); - output.push_str("\n"); - - for (prop_name, _) in &prop_other_component_refs { - output.push_str(&format!(" let mut {}: Option<_> = None;\n", prop_name)); - } - - for (prop_name, _) in &prop_union_component_refs { - output.push_str(&format!(" let mut {}: Vec<_> = vec![];\n", prop_name)); - } - - for per_type_component_ref in &per_type_component_refs { - output.push_str(&format!( - " let mut {}: Option<_> = None;\n", - per_type_component_ref.component_internal_name - )); - } - - if !ordered_members.is_empty() { - output.push_str(" let mut ordered_members = vec![];\n"); - } - - if has_text { - output.push_str(" let mut text = vec![];\n"); - } - - output.push_str("\n"); - - if has_content { - output.push_str(" while let Some(member) = members.pop() {\n"); - output.push_str(" match member {\n"); - - for (prop_name, prop_union_component_refs) in &prop_union_component_refs { - for (index, prop_union_component_ref) in - prop_union_component_refs.iter().enumerate() - { - output.push_str(&format!( - " {}WidgetMembersOwned::{}(widget) => {{\n", - name, prop_union_component_ref.component_name - )); - output.push_str(&format!( - " {}.push({}{}::_{}(widget));\n", - prop_name, - name, - prop_name.to_case(Case::Pascal), - index - )); - output.push_str(&format!(" }}\n")); - } - } - - for (prop_name, prop_other_component_refs) in &prop_other_component_refs { - output.push_str(&format!( - " {}WidgetMembersOwned::{}(widget) => {{\n", - name, prop_other_component_refs.component_name - )); - output - .push_str(&format!(" if let Some(_) = {} {{\n", prop_name)); - output.push_str(&format!(" return Err(Error::custom(\"Only one {} is allowed\"))\n", prop_other_component_refs.component_name)); - output.push_str(&format!(" }}\n")); - output.push_str(&format!(" {} = Some(widget);\n", prop_name)); - output.push_str(&format!(" }}\n")); - } - - for per_type_component_ref in &per_type_component_refs { - output.push_str(&format!( - " {}WidgetMembersOwned::{}(widget) => {{\n", - name, per_type_component_ref.component_name - )); - output.push_str(&format!( - " if let Some(_) = {} {{\n", - per_type_component_ref.component_internal_name - )); - output.push_str(&format!(" return Err(Error::custom(\"Only one {} is allowed\"))\n", per_type_component_ref.component_name)); - output.push_str(&format!(" }}\n")); - output.push_str(&format!( - " {} = Some(widget);\n", - per_type_component_ref.component_internal_name - )); - output.push_str(&format!(" }}\n")); - } - - for ordered_component_ref in &unique_ordered_component_refs { - output.push_str(&format!( - " {}WidgetMembersOwned::{}(widget) => {{\n", - name, ordered_component_ref.component_name - )); - output.push_str(&format!(" ordered_members.insert(0, {}WidgetOrderedMembers::{}(widget));\n", name, ordered_component_ref.component_name)); - output.push_str(&format!(" }}\n")); - } - - if has_text { - output.push_str(&format!( - " {}WidgetMembersOwned::Text {{ value }} => {{\n", - name - )); - output.push_str(&format!(" text.insert(0, value);\n")); - output.push_str(&format!(" }}\n")); - } - - output.push_str(" }\n"); - output.push_str(" }\n"); - } - - output.push_str("\n"); - output.push_str(&format!(" Ok({}WidgetContent {{\n", name)); - - for (prop_name, _) in &prop_union_component_refs { - output.push_str(&format!(" {},\n", prop_name)); - } - - for per_type_component_ref in &per_type_component_refs { - match per_type_component_ref.arity { - Arity::ZeroOrOne => { - output.push_str(&format!( - " {},\n", - per_type_component_ref.component_internal_name - )); - } - Arity::One => { - output.push_str(&format!( - " {}: {}.ok_or(Error::custom(\"{} is required\"))?,\n", - per_type_component_ref.component_internal_name, - per_type_component_ref.component_internal_name, - per_type_component_ref.component_name - )); - } - Arity::ZeroOrMore => { - todo!() - } - } - } - - for (prop_name, prop_other_component_ref) in &prop_other_component_refs { - match prop_other_component_ref.arity { - Arity::ZeroOrOne => { - output.push_str(&format!(" {},\n", prop_name)); - } - Arity::One => { - output.push_str(&format!( - " {}: {}.ok_or(Error::custom(\"{} is required\"))?,\n", - prop_name, - prop_other_component_ref.component_internal_name, - prop_other_component_ref.component_name - )); - } - Arity::ZeroOrMore => { - todo!() - } - } - } - - if !ordered_members.is_empty() { - output.push_str(" ordered_members\n"); - } - - if has_text { - output.push_str(" text\n"); - } - - output.push_str(&format!(" }})\n")); - output.push_str(&format!(" }}\n")); - output.push_str(&format!("}}\n")); - } - } - } - - output.push_str("#[derive(Debug, Deserialize, Encode, Decode)]\n"); - output.push_str(&format!("pub struct {}Widget {{\n", name)); - output.push_str(" #[serde(rename = \"__id__\")]\n"); - output.push_str(" pub __id__: UiWidgetId,\n"); - - for prop in props { - if matches!(prop.property_type.kind(), PropertyKind::Property) { - output.push_str(&format!(" #[serde(rename = \"{}\")]\n", prop.name)); - output.push_str(&format!( - " pub {}: {},\n", - prop.name.to_case(Case::Snake), - generate_type(&prop, name) - )); - } - } - - if has_content { - output.push_str(&format!(" pub content: {}WidgetContent,\n", name)); - } - - output.push_str("}\n"); - - let generate_union = |output: &mut String, items: &Vec, prop_name: &String| { - output.push_str("#[derive(Debug, Encode, Decode)]\n"); - output.push_str(&format!("pub enum {}{} {{\n", name, prop_name.to_case(Case::Pascal))); - - for (index, property_type) in items.iter().enumerate() { - output.push_str(&format!( - " _{}({}),\n", - index, - generate_required_type(&property_type, None) - )); - } - - output.push_str("}\n"); - output.push_str("\n"); - }; - - for prop in props { - match &prop.property_type { - PropertyType::Union { items } => generate_union(&mut output, items, &prop.name), - PropertyType::Array { item } => { - match item.deref() { - PropertyType::Union { items } => generate_union(&mut output, items, &prop.name), - _ => {} - } - } - _ => {} - } - } - } - Component::Root { - children, shared_types, .. - } => { - for (type_name, shared_type) in shared_types { - match shared_type { - SharedType::Enum { items } => { - output.push_str("#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]\n"); - output.push_str(&format!("pub enum {} {{\n", type_name)); - - for item in items { - output.push_str(&format!(" #[serde(rename = \"{}\")]\n", &item)); - output.push_str(&format!(" {},\n", &item)); - } - - output.push_str("}\n"); - output.push_str("\n"); - } - SharedType::Object { items } => { - output.push_str("#[derive(Debug, Deserialize, Encode, Decode)]\n"); - output.push_str(&format!("pub struct {} {{\n", type_name)); - - for (property_name, property_type) in items { - output.push_str(&format!( - " pub {}: {},\n", - &property_name, - generate_required_type( - &property_type, - Some(format!("{}{}", type_name, property_name)) - ) - )); - } - - output.push_str("}\n"); - output.push_str("\n"); - } - SharedType::Union { items } => { - output.push_str("#[derive(Debug, Deserialize, Encode, Decode)]\n"); - output.push_str("#[serde(untagged)]\n"); - output.push_str(&format!("pub enum {} {{\n", type_name)); - - for property_type in items { - match property_type { - PropertyType::SharedTypeRef { name } => { - output.push_str(&format!(" {}({}),\n", &name, name)); - } - _ => { - todo!() - } - } - } - - output.push_str("}\n"); - output.push_str("\n"); - } - } - } - - output.push_str("#[derive(Debug, Deserialize, Encode, Decode)]\n"); - output.push_str("#[serde(tag = \"__type__\")]\n"); - output.push_str("pub enum RootWidgetMembers {\n"); - - for component_ref in children { - output.push_str(&format!( - " #[serde(rename = \"gauntlet:{}\")]\n", - component_ref.component_internal_name - )); - output.push_str(&format!( - " {}({}Widget),\n", - component_ref.component_name, component_ref.component_name - )); - } - - output.push_str("}\n"); - - output.push_str("#[derive(Debug, Deserialize, Encode, Decode)]\n"); - output.push_str("pub struct RootWidget {\n"); - output.push_str(" #[serde(default, deserialize_with = \"array_to_option\")]\n"); - output.push_str(" pub content: Option\n"); - output.push_str("}\n"); - } - Component::TextPart { .. } => {} - } - } - - generate_file(&dest_path, &output)?; - - Ok(()) -} - -fn generate_file>(path: P, text: &str) -> std::io::Result<()> { - let mut f = File::create(path)?; - f.write_all(text.as_bytes()) -} - -fn generate_type(property: &Property, name: &ComponentName) -> String { - match property.optional { - true => { - generate_optional_type( - &property.property_type, - format!("{}{}", name, &property.name.to_case(Case::Pascal)), - ) - } - false => { - generate_required_type( - &property.property_type, - Some(format!("{}{}", name, &property.name.to_case(Case::Pascal))), - ) - } - } -} - -fn generate_optional_type(property_type: &PropertyType, union_name: String) -> String { - format!("Option<{}>", generate_required_type(property_type, Some(union_name))) -} - -fn generate_required_type(property_type: &PropertyType, union_name: Option) -> String { - match property_type { - PropertyType::String => "String".to_owned(), - PropertyType::Number => "f64".to_owned(), - PropertyType::Boolean => "bool".to_owned(), - PropertyType::Function { .. } => panic!("client doesn't know about functions in properties"), - PropertyType::Component { reference } => format!("{}Widget", reference.component_name.to_string()), - PropertyType::SharedTypeRef { name } => name.to_owned(), - PropertyType::Union { .. } => { - match union_name { - None => panic!("should not be used"), - Some(union_name) => union_name, - } - } - PropertyType::Array { item } => format!("Vec<{}>", generate_required_type(item, union_name)), - } -} diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index 083b82c..a988dad 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -10,9 +10,7 @@ use bincode::Encode; use gix_url::Scheme; use gix_url::Url; use serde::Deserialize; -use serde::Deserializer; use serde::Serialize; -use serde::de::Error; #[derive(Debug, Clone, PartialEq, Eq, Hash, Encode, Decode)] pub struct PluginId(Arc); @@ -233,26 +231,7 @@ pub enum KeyboardEventOrigin { PluginView, } -fn array_to_option<'de, D, V>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, - V: Deserialize<'de>, -{ - let res = Option::>::deserialize(deserializer)?; - - match res { - None => Ok(None), - Some(mut res) => { - match res.len() { - 0 => Ok(None), - 1 => Ok(Some(res.remove(0))), - _ => Err(Error::custom("only zero or one allowed")), - } - } - } -} - -include!(concat!(env!("OUT_DIR"), "/components.rs")); +gauntlet_utils_macros::widget_model_gen!(); // TODO generate this #[allow(async_fn_in_trait)] diff --git a/rust/plugin_runtime/Cargo.toml b/rust/plugin_runtime/Cargo.toml index f2f3f18..88f7cfb 100644 --- a/rust/plugin_runtime/Cargo.toml +++ b/rust/plugin_runtime/Cargo.toml @@ -8,6 +8,7 @@ edition.workspace = true gauntlet-common.workspace = true gauntlet-component-model.workspace = true gauntlet-utils.workspace = true +gauntlet-utils-macros.workspace = true gauntlet-common-plugin-runtime.workspace = true # shared diff --git a/rust/plugin_runtime/src/lib.rs b/rust/plugin_runtime/src/lib.rs index 216daa4..186141a 100644 --- a/rust/plugin_runtime/src/lib.rs +++ b/rust/plugin_runtime/src/lib.rs @@ -7,6 +7,7 @@ mod environment; mod events; mod logs; mod model; +mod model_deserialization; mod permissions; mod plugin_data; mod plugins; diff --git a/rust/plugin_runtime/src/model_deserialization.rs b/rust/plugin_runtime/src/model_deserialization.rs new file mode 100644 index 0000000..81eccad --- /dev/null +++ b/rust/plugin_runtime/src/model_deserialization.rs @@ -0,0 +1,238 @@ +use deno_core::v8; +use gauntlet_common::model::*; + +type Result = std::result::Result; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("invalid type (expected {expected:?}, got {found:?}) at path '{}'", .path.join("."))] + UnexpectedType { + expected: &'static str, + found: &'static str, + path: Vec, + }, + #[error("unexpected component (expected one of [{expected:?}], got {found:?}) at path '{}'", .path.join("."))] + UnexpectedComponent { + found: String, + expected: &'static str, + path: Vec, + }, + #[error("enum '{enum_name}' does not accept '{value}' as one of it's values, at path '{}'", .path.join("."))] + InvalidEnumValue { + enum_name: &'static str, + value: String, + path: Vec, + }, + #[error("required property '{name}' is not provided at path '{}'", .path.join("."))] + RequiredProp { name: &'static str, path: Vec }, + #[error("only single '{name}' component can be specified at path '{}'", .path.join("."))] + SingleComponent { name: &'static str, path: Vec }, + #[error("required component '{name}' is not provided at path '{}'", .path.join("."))] + RequiredComponent { name: &'static str, path: Vec }, + #[error("unknown property(ies) [{}] at path '{}'", .names.join(", "), .path.join("."))] + UnknownProp { names: Vec, path: Vec }, + #[error("internal: {msg} at path '{}'", .path.join("."))] + InternalError { msg: String, path: Vec }, + #[error("internal: {msg} at path '{}'", .path.join("."))] + InternalErrorWithSource { + msg: String, + path: Vec, + #[source] + source: Box, + }, +} + +macro_rules! error_internal { + ($($arg:tt)*) => { + Error::internal(format!($($arg)*)) + }; +} +macro_rules! error_internal_source { + ($err:expr, $($arg:tt)*) => { + Error::internal_with_source(format!($($arg)*), Box::new($err)) + }; +} + +impl Error { + fn unexpected_type(found: &'static str, expected: &'static str) -> Self { + Self::UnexpectedType { + expected, + found, + path: vec![], + } + } + + fn unexpected_component(found: &str, expected: &'static str) -> Self { + Self::UnexpectedComponent { + found: found.to_string(), + expected, + path: vec![], + } + } + + fn unknown_prop(names: Vec) -> Self { + Self::UnknownProp { names, path: vec![] } + } + + fn required_prop(name: &'static str) -> Self { + Self::RequiredProp { name, path: vec![] } + } + + fn required_component(name: &'static str) -> Self { + Self::RequiredComponent { name, path: vec![] } + } + + fn single_component(name: &'static str) -> Self { + Self::SingleComponent { name, path: vec![] } + } + + fn invalid_enum_value(enum_name: &'static str, value: &str) -> Self { + Self::InvalidEnumValue { + enum_name, + value: value.to_string(), + path: vec![], + } + } + + fn internal(msg: impl Into) -> Self { + Self::InternalError { + msg: msg.into(), + path: vec![], + } + } + + fn internal_with_source(msg: impl Into, source: Box) -> Self { + Self::InternalErrorWithSource { + msg: msg.into(), + path: vec![], + source, + } + } + + fn inside(path_segment: &'static str) -> impl Fn(Error) -> Error { + fn prepend(path: &mut Vec, path_segment: &'static str) { + path.insert(0, path_segment.to_string()); + } + + move |mut err: Error| { + match &mut err { + Error::UnexpectedType { path, .. } => prepend(path, path_segment), + Error::InternalError { path, .. } => prepend(path, path_segment), + Error::InternalErrorWithSource { path, .. } => prepend(path, path_segment), + Error::UnknownProp { path, .. } => prepend(path, path_segment), + Error::RequiredProp { path, .. } => prepend(path, path_segment), + Error::SingleComponent { path, .. } => prepend(path, path_segment), + Error::RequiredComponent { path, .. } => prepend(path, path_segment), + Error::UnexpectedComponent { path, .. } => prepend(path, path_segment), + Error::InvalidEnumValue { path, .. } => prepend(path, path_segment), + } + + err + } + } +} + +fn deserialize_widget_type(scope: &mut v8::HandleScope, container: v8::Local) -> Result { + let widget_type = extract_object_value(scope, container, "widgetType") + .ok_or(error_internal!("'widgetType' field is not present on widget object"))?; + + let widget_type: v8::Local = widget_type.try_into().map_err(|_| { + error_internal!( + "invalid 'widgetType', expected 'string', got: '{}'", + widget_type.type_repr() + ) + })?; + + Ok(widget_type.to_rust_string_lossy(scope)) +} + +fn deserialize_boolean(value: v8::Local) -> Result { + if value.is_boolean() { + Ok(value.is_true()) + } else { + Err(Error::unexpected_type(value.type_repr(), "boolean")) + } +} + +fn deserialize_string(scope: &mut v8::HandleScope, value: v8::Local) -> Result { + let value: v8::Local = value + .try_into() + .map_err(|_| Error::unexpected_type(value.type_repr(), "string"))?; + + Ok(value.to_rust_string_lossy(scope)) +} + +fn deserialize_number(value: v8::Local) -> Result { + let value: v8::Local = value + .try_into() + .map_err(|_| Error::unexpected_type(value.type_repr(), "number"))?; + + Ok(value.value()) +} + +fn deserialize_text_widget(scope: &mut v8::HandleScope, container: v8::Local) -> Result { + let value = extract_object_value(scope, container, "widgetProperties") + .ok_or_else(|| error_internal!("'widgetProperties' field not present on widget object"))?; + + let value: v8::Local = value.try_into().map_err(|_| { + error_internal!( + "invalid 'widgetProperties', expected 'object', got: '{}'", + value.type_repr() + ) + })?; + + let value = extract_object_value(scope, value, "value") + .ok_or_else(|| error_internal!("'value' field not present on widget object"))?; + + let value: v8::Local = value + .try_into() + .map_err(|_| error_internal!("invalid 'value', expected 'string', got: '{}'", value.type_repr()))?; + + Ok(value.to_rust_string_lossy(scope)) +} + +fn extract_object_value<'a, 'b: 'a>( + scope: &mut v8::HandleScope<'b>, + object: v8::Local, + key: &str, +) -> Option> { + let key = v8::String::new(scope, key).unwrap(); + let value = object.get(scope, key.into()); + value.filter(|value| !value.is_null_or_undefined()) +} + +fn extract_object_keys(scope: &mut v8::HandleScope, object: v8::Local) -> Result> { + let names_args = v8::GetPropertyNamesArgsBuilder::new() + .key_conversion(v8::KeyConversionMode::ConvertToString) + .build(); + + let keys = match object.get_own_property_names(scope, names_args) { + Some(keys) => { + let mut result = vec![]; + + for index in 0..keys.length() { + let key = keys + .get_index(scope, index) + .ok_or(error_internal!("unable to get item from array at index {}", index))?; + + let key: v8::Local = key.try_into().map_err(|_| { + error_internal!( + "get_own_property_names array item is not a string, got: {}", + key.type_repr() + ) + })?; + + let key = key.to_rust_string_lossy(scope); + + result.push(key) + } + + result + } + None => vec![], + }; + + Ok(keys) +} + +gauntlet_utils_macros::widget_deserialization_gen!(); diff --git a/rust/plugin_runtime/src/ui.rs b/rust/plugin_runtime/src/ui.rs index 1c2d495..43ff067 100644 --- a/rust/plugin_runtime/src/ui.rs +++ b/rust/plugin_runtime/src/ui.rs @@ -5,20 +5,18 @@ use std::rc::Rc; use anyhow::anyhow; use deno_core::OpState; use deno_core::op2; -use deno_core::serde_v8; use deno_core::v8; use futures::executor::block_on; use gauntlet_common::model::EntrypointId; -use gauntlet_common::model::RootWidget; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use gauntlet_common_plugin_runtime::model::JsUiRenderLocation; use gauntlet_component_model::Component; -use serde::Deserialize; use tokio::runtime::Handle; use crate::component_model::ComponentModel; use crate::deno::GauntletJsError; +use crate::model_deserialization::deserialize_root_widget; use crate::plugin_data::PluginData; #[op2] @@ -98,10 +96,8 @@ pub fn op_react_replace_view<'a>( ) -> Result<(), GauntletJsError> { tracing::trace!(target = "renderer_rs", "Calling op_react_replace_view..."); - let mut deserializer = serde_v8::Deserializer::new(scope, container, None); - - let container = RootWidget::deserialize(&mut deserializer) - .map_err(|err| anyhow!("Unable to deserialize root widget: {:#}", err))?; + let container = deserialize_root_widget(scope, container) + .map_err(|err| anyhow!("Unable to deserialize component tree: {:#}", err))?; let entrypoint_id = EntrypointId::from_string(entrypoint_id); let entrypoint_name = entrypoint_name.to_string(); diff --git a/rust/utils_macros/Cargo.toml b/rust/utils_macros/Cargo.toml index 99b628f..b9e15e2 100644 --- a/rust/utils_macros/Cargo.toml +++ b/rust/utils_macros/Cargo.toml @@ -11,3 +11,6 @@ quote = "1.0" syn = "2.0" convert_case = "0.8.0" proc-macro2 = "1.0.92" +gauntlet-component-model.workspace = true +indexmap.workspace = true +itertools.workspace = true diff --git a/rust/utils_macros/src/lib.rs b/rust/utils_macros/src/lib.rs index 8c5bdbf..02bde82 100644 --- a/rust/utils_macros/src/lib.rs +++ b/rust/utils_macros/src/lib.rs @@ -2,6 +2,8 @@ use proc_macro::TokenStream; mod boundary_gen; mod rusqlite; +mod widget_deserialization_gen; +mod widget_model_gen; #[proc_macro_attribute] pub fn boundary_gen(args: TokenStream, input: TokenStream) -> TokenStream { @@ -12,3 +14,13 @@ pub fn boundary_gen(args: TokenStream, input: TokenStream) -> TokenStream { pub fn derive_rusqlite(item: TokenStream) -> TokenStream { rusqlite::derive_rusqlite(item) } + +#[proc_macro] +pub fn widget_model_gen(_item: TokenStream) -> TokenStream { + widget_model_gen::widget_model_gen() +} + +#[proc_macro] +pub fn widget_deserialization_gen(_item: TokenStream) -> TokenStream { + widget_deserialization_gen::widget_deserialization_gen() +} diff --git a/rust/utils_macros/src/widget_deserialization_gen.rs b/rust/utils_macros/src/widget_deserialization_gen.rs new file mode 100644 index 0000000..11f6453 --- /dev/null +++ b/rust/utils_macros/src/widget_deserialization_gen.rs @@ -0,0 +1,835 @@ +use convert_case::Case; +use convert_case::Casing; +use gauntlet_component_model::Arity; +use gauntlet_component_model::Children; +use gauntlet_component_model::Component; +use gauntlet_component_model::ComponentRef; +use gauntlet_component_model::PropertyKind; +use gauntlet_component_model::PropertyType; +use gauntlet_component_model::SharedType; +use gauntlet_component_model::create_component_model; +use indexmap::IndexMap; +use itertools::Itertools; +use proc_macro::TokenStream; +use quote::quote; + +pub fn widget_deserialization_gen() -> TokenStream { + let components = create_component_model(); + + let mut output_components = vec![]; + + for component in &components { + match component { + Component::Standard { + internal_name, + name, + props, + children, + .. + } => { + let name = name.to_string(); + let props_has_content = props + .iter() + .any(|prop| matches!(prop.property_type.kind(), PropertyKind::Component)); + + let has_props = props + .iter() + .any(|prop| !matches!(prop.property_type.kind(), PropertyKind::Component)); + + let children_has_content = match children { + Children::Members { + ordered_members, + per_type_members, + .. + } + | Children::StringOrMembers { + ordered_members, + per_type_members, + .. + } => !ordered_members.is_empty() || !per_type_members.is_empty(), + _ => false, + }; + + let has_text = matches!(children, Children::StringOrMembers { .. } | Children::String { .. }); + + let has_content = children_has_content || props_has_content || has_text; + + let default = IndexMap::new(); + + let (ordered_members, per_type_members) = match children { + Children::Members { + ordered_members, + per_type_members, + .. + } + | Children::StringOrMembers { + ordered_members, + per_type_members, + .. + } => (ordered_members, per_type_members), + _ => (&default, &default), + }; + + let deserialize_method = format!("deserialize_{}_widget", internal_name); + let deserialize_method = syn::Ident::new(&deserialize_method, proc_macro2::Span::call_site()); + + let deserialize_content_method = format!("deserialize_{}_widget_content", internal_name); + let deserialize_content_method = + syn::Ident::new(&deserialize_content_method, proc_macro2::Span::call_site()); + + let (props_names, widget_properties) = if has_props { + let props_names: Vec<_> = props + .iter() + .filter(|prop| matches!(prop.property_type.kind(), PropertyKind::Property)) + .map(|prop| { + let name = syn::Ident::new(&prop.name.to_case(Case::Snake), proc_macro2::Span::call_site()); + quote! { + #name, + } + }) + .collect(); + + let props_deserialization: Vec<_> = props + .iter() + .map(|prop| { + let prop_name = &prop.name; + + let prop_var_name = prop_name.to_case(Case::Snake); + let prop_var_name = syn::Ident::new(&prop_var_name, proc_macro2::Span::call_site()); + + match prop.property_type.kind() { + PropertyKind::Event | PropertyKind::Component => { + quote! { + keys.retain(|item| *item != #prop_name); + } + } + PropertyKind::Property => { + let deserialize_prop = match &prop.property_type { + PropertyType::String => { + quote! { + deserialize_string(scope, property_value)? + } + } + PropertyType::Number => { + quote! { + deserialize_number(property_value)? + } + } + PropertyType::Boolean => { + quote! { + deserialize_boolean(property_value)? + } + } + PropertyType::Component { .. } => panic!(), + PropertyType::Function { .. } => panic!(), + PropertyType::SharedTypeRef { name } => { + let method_name = format!("deserialize_shared_type_{}", name.to_case(Case::Snake)); + let method_name = syn::Ident::new(&method_name, proc_macro2::Span::call_site()); + + quote! { + #method_name(scope, property_value)? + } + } + PropertyType::Union { .. } | PropertyType::Array { .. } => { + panic!() // only property kind property is used here + } + }; + + if prop.optional { + quote! { + keys.retain(|item| *item != #prop_name); + let #prop_var_name = match extract_object_value(scope, widget_properties, #prop_name) { + Some(property_value) => Some(#deserialize_prop), + None => None, + }; + } + } else { + quote! { + keys.retain(|item| *item != #prop_name); + let #prop_var_name = match extract_object_value(scope, widget_properties, #prop_name) { + Some(property_value) => #deserialize_prop, + None => { + return Err(Error::required_prop(#prop_name)) + }, + }; + } + } + } + } + }) + .collect(); + + let widget_properties = quote! { + let widget_properties = extract_object_value(scope, container, "widgetProperties") + .ok_or(error_internal!("'widgetProperties' field is not present on widget object"))?; + + let widget_properties: v8::Local = widget_properties + .try_into() + .map_err(|_| error_internal!("invalid 'widget_properties', expected 'object', got: '{}'", widget_properties.type_repr()))?; + + let mut keys = extract_object_keys(scope, widget_properties) + .map_err(|err| error_internal_source!(err, "unable to get keys of 'widget_properties' object"))?; + + #(#props_deserialization)* + + match keys.len() { + 0 => {} + _ => return Err(Error::unknown_prop(keys)), + } + }; + + (props_names, Some(widget_properties)) + } else { + (vec![], None) + }; + + let (widget_children, content) = if has_content { + let widget_children = quote! { + let widget_children = extract_object_value(scope, container, "widgetChildren") + .ok_or(error_internal!("'widgetChildren' field is not present on widget object"))?; + + let widget_children: v8::Local = widget_children + .try_into() + .map_err(|_| error_internal!("invalid 'widgetChildren', expected 'array', got: '{}'", widget_children.type_repr()))?; + }; + + let content = quote! { + content: #deserialize_content_method(scope, widget_children) + .map_err(Error::inside(#name))?, + }; + + (Some(widget_children), Some(content)) + } else { + (None, None) + }; + + let widget_struct_name = format!("{}Widget", name); + let widget_struct_name = syn::Ident::new(&widget_struct_name, proc_macro2::Span::call_site()); + + output_components.push(quote! { + fn #deserialize_method( + scope: &mut v8::HandleScope, + container: v8::Local, + ) -> Result<#widget_struct_name> { + let widget_id = extract_object_value(scope, container, "widgetId") + .ok_or(error_internal!("'widgetId' field is not present on widget object"))?; + + let widget_id: v8::Local = widget_id + .try_into() + .map_err(|_| error_internal!("invalid 'widgetId', expected 'string', got: '{}'", widget_id.type_repr()))?; + + let widget_id = widget_id.value() as usize; + + #widget_children + + #widget_properties + + Ok(#widget_struct_name { + __id__: widget_id, + #(#props_names)* + #content + }) + } + }); + + if has_content { + let ordered_component_refs = ordered_members + .iter() + .map(|(_member_name, component_ref)| component_ref) + .unique_by(|component_ref| component_ref.component_name.clone()) + .collect::>(); + + let per_type_component_refs = per_type_members + .iter() + .map(|(_member_name, component_ref)| component_ref) + .collect::>(); + + let mut prop_union_component_refs = IndexMap::new(); + let mut prop_other_component_refs = IndexMap::new(); + + for prop in props { + let is_union = match &prop.property_type { + PropertyType::Union { .. } => true, + PropertyType::Array { item } => matches!(item.as_ref(), PropertyType::Union { .. }), + _ => false, + }; + + let prop_name = prop.name.to_case(Case::Snake); + + if is_union { + fn all_component_refs(property_type: &PropertyType) -> Vec<&ComponentRef> { + match property_type { + PropertyType::String => vec![], + PropertyType::Number => vec![], + PropertyType::Boolean => vec![], + PropertyType::Component { reference } => vec![reference], + PropertyType::Function { .. } => vec![], + PropertyType::SharedTypeRef { .. } => vec![], + PropertyType::Union { items } => { + items.iter().flat_map(|prop| all_component_refs(prop)).collect() + } + PropertyType::Array { item } => all_component_refs(item), + } + } + + prop_union_component_refs.insert(prop_name, all_component_refs(&prop.property_type)); + } else { + match &prop.property_type { + PropertyType::Component { reference } => { + prop_other_component_refs.insert(prop_name, reference); + } + PropertyType::Array { item, .. } => { + match item.as_ref() { + PropertyType::Component { reference } => { + prop_other_component_refs.insert(prop_name, reference); + } + _ => {} + } + } + _ => {} + } + } + } + + let union_component_props: Vec<_> = prop_union_component_refs + .iter() + .map(|(prop_name, _)| { + let prop_name = syn::Ident::new(&prop_name, proc_macro2::Span::call_site()); + + quote! { + let mut #prop_name: Vec<_> = vec![]; + } + }) + .collect(); + + let union_component_props_match_arms: Vec<_> = prop_union_component_refs + .iter() + .flat_map(|(prop_name, prop_union_component_refs)| { + prop_union_component_refs + .iter() + .enumerate() + .map(|(index, prop_union_component_ref)| { + let id = &prop_union_component_ref.component_internal_name; + let react_id = format!("gauntlet:{}", id); + + let deserialize_method_name = format!("deserialize_{}_widget", id); + let deserialize_method_name = + syn::Ident::new(&deserialize_method_name, proc_macro2::Span::call_site()); + + let union_enum_name = format!("{}{}", name, prop_name.to_case(Case::Pascal)); + let union_enum_name = + syn::Ident::new(&union_enum_name, proc_macro2::Span::call_site()); + + let union_enum_item_name = format!("_{}", index); + let union_enum_item_name = + syn::Ident::new(&union_enum_item_name, proc_macro2::Span::call_site()); + + let prop_name = syn::Ident::new(&prop_name, proc_macro2::Span::call_site()); + + quote! { + #react_id => { + let widget = #deserialize_method_name(scope, container)?; + + #prop_name.push(#union_enum_name::#union_enum_item_name(widget)) + } + } + }) + .collect::>() + }) + .collect(); + + let prop_union_results: Vec<_> = prop_union_component_refs + .iter() + .map(|(prop_name, _)| { + let prop_name = syn::Ident::new(&prop_name, proc_macro2::Span::call_site()); + quote! { + #prop_name, + } + }) + .collect(); + + let other_component_props: Vec<_> = prop_other_component_refs + .iter() + .map(|(prop_name, _)| { + let prop_name = syn::Ident::new(&prop_name, proc_macro2::Span::call_site()); + + quote! { + let mut #prop_name: Option<_> = None; + } + }) + .collect(); + + let other_component_props_match_arms: Vec<_> = prop_other_component_refs + .iter() + .map(|(prop_name, prop_other_component_ref)| { + let name = &prop_other_component_ref.component_name.to_string(); + let id = &prop_other_component_ref.component_internal_name; + let react_id = format!("gauntlet:{}", id); + let prop_name = syn::Ident::new(&prop_name, proc_macro2::Span::call_site()); + + let deserialize_method_name = format!("deserialize_{}_widget", id); + let deserialize_method_name = + syn::Ident::new(&deserialize_method_name, proc_macro2::Span::call_site()); + + quote! { + #react_id => { + if let Some(_) = #prop_name { + return Err(Error::single_component(#name)); + } + + let widget = #deserialize_method_name(scope, container)?; + + #prop_name = Some(widget); + } + } + }) + .collect(); + + let prop_other_results: Vec<_> = prop_other_component_refs + .iter() + .map(|(prop_name, prop_other_component_ref)| { + let prop_name = syn::Ident::new(&prop_name, proc_macro2::Span::call_site()); + match prop_other_component_ref.arity { + Arity::ZeroOrOne => { + quote! { + #prop_name, + } + } + Arity::One => { + let component_name = &prop_other_component_ref.component_name.to_string(); + quote! { + #prop_name: #prop_name.ok_or(Error::required_component(#component_name))?, + } + } + Arity::ZeroOrMore => { + unimplemented!() + } + } + }) + .collect(); + + let per_type_component_props: Vec<_> = per_type_component_refs + .iter() + .map(|per_type_component_ref| { + let prop_name = &per_type_component_ref.component_internal_name; + let prop_name = syn::Ident::new(prop_name, proc_macro2::Span::call_site()); + + quote! { + let mut #prop_name: Option<_> = None; + } + }) + .collect(); + + let per_type_component_props_match_arms: Vec<_> = per_type_component_refs + .iter() + .map(|prop_other_component_ref| { + let name = &prop_other_component_ref.component_name.to_string(); + let id = &prop_other_component_ref.component_internal_name; + let react_id = format!("gauntlet:{}", id); + let prop_name = syn::Ident::new(&id, proc_macro2::Span::call_site()); + + let deserialize_method_name = format!("deserialize_{}_widget", id); + let deserialize_method_name = + syn::Ident::new(&deserialize_method_name, proc_macro2::Span::call_site()); + + quote! { + #react_id => { + if let Some(_) = #prop_name { + return Err(Error::single_component(#name)); + } + + let widget = #deserialize_method_name(scope, container)?; + + #prop_name = Some(widget); + } + } + }) + .collect(); + + let per_type_prop_results: Vec<_> = per_type_component_refs + .iter() + .map(|per_type_component_ref| { + let prop_name = &per_type_component_ref.component_internal_name; + let prop_name = syn::Ident::new(prop_name, proc_macro2::Span::call_site()); + match per_type_component_ref.arity { + Arity::ZeroOrOne => { + quote! { + #prop_name, + } + } + Arity::One => { + let component_name = &per_type_component_ref.component_name.to_string(); + quote! { + #prop_name: #prop_name.ok_or(Error::required_component(#component_name))?, + } + } + Arity::ZeroOrMore => { + unimplemented!() + } + } + }) + .collect(); + + let (ordered_members_prop, ordered_members_prop_match_arms, ordered_members_prop_result) = + if !ordered_members.is_empty() { + let ordered_members_prop = quote! { + let mut ordered_members = vec![]; + }; + + let ordered_members_prop_match_arms: Vec<_> = ordered_component_refs + .iter() + .map(|ordered_component_ref| { + let id = &ordered_component_ref.component_internal_name; + let react_id = format!("gauntlet:{}", id); + + let ordered_members_enum_name = format!("{}WidgetOrderedMembers", name); + let ordered_members_enum_name = + syn::Ident::new(&ordered_members_enum_name, proc_macro2::Span::call_site()); + + let ordered_members_enum_item_name = &ordered_component_ref.component_name.to_string(); + let ordered_members_enum_item_name = + syn::Ident::new(&ordered_members_enum_item_name, proc_macro2::Span::call_site()); + + let deserialize_method_name = format!("deserialize_{}_widget", id); + let deserialize_method_name = + syn::Ident::new(&deserialize_method_name, proc_macro2::Span::call_site()); + + quote! { + #react_id => { + let widget = #deserialize_method_name(scope, container)?; + + ordered_members.push(#ordered_members_enum_name::#ordered_members_enum_item_name(widget)); + } + } + }) + .collect(); + + let ordered_members_prop_result = quote! { + ordered_members, + }; + + ( + Some(ordered_members_prop), + ordered_members_prop_match_arms, + Some(ordered_members_prop_result), + ) + } else { + (None, vec![], None) + }; + + let (text_prop, text_prop_match_arm, text_prop_result) = if has_text { + let text_prop = quote! { + let mut text = vec![]; + }; + + let text_react_id = "gauntlet:text_part"; + let text_prop_match_arm = quote! { + #text_react_id => { + let widget_text = deserialize_text_widget(scope, container)?; + + text.push(widget_text); + } + }; + + let text_prop_result = quote! { + text, + }; + + (Some(text_prop), Some(text_prop_match_arm), Some(text_prop_result)) + } else { + (None, None, None) + }; + + let mut expected_types = vec![]; + + for (_, prop_union_component_refs) in prop_union_component_refs { + for prop_union_component_ref in prop_union_component_refs { + expected_types.push(prop_union_component_ref.component_internal_name.to_string()); + } + } + + for (_, prop_other_component_refs) in prop_other_component_refs { + expected_types.push(prop_other_component_refs.component_internal_name.to_string()); + } + + for per_type_component_ref in per_type_component_refs { + expected_types.push(per_type_component_ref.component_internal_name.to_string()); + } + + for ordered_component_ref in ordered_component_refs { + expected_types.push(ordered_component_ref.component_internal_name.to_string()); + } + + if has_text { + expected_types.push("gauntlet:text_part".to_string()) + } + let expected_types = expected_types.join(", "); + + let widget_content_struct_name = format!("{}WidgetContent", name); + let widget_content_struct_name = + syn::Ident::new(&widget_content_struct_name, proc_macro2::Span::call_site()); + + output_components.push(quote! { + fn #deserialize_content_method( + scope: &mut v8::HandleScope, + widget_children: v8::Local, + ) -> Result<#widget_content_struct_name> { + #(#union_component_props)* + #(#other_component_props)* + #(#per_type_component_props)* + #ordered_members_prop + #text_prop + + for index in 0..widget_children.length() { + let container = widget_children + .get_index(scope, index) + .ok_or(error_internal!("unable to get item from 'widget_children' array at index {}", index))?; + + let container: v8::Local = container + .try_into() + .map_err(|_| error_internal!("invalid widget container, expected 'object', got: '{}'", container.type_repr()))?; + + let widget_type = deserialize_widget_type(scope, container)?; + + match widget_type.as_str() { + #(#union_component_props_match_arms)* + #(#other_component_props_match_arms)* + #(#per_type_component_props_match_arms)* + #(#ordered_members_prop_match_arms)* + #text_prop_match_arm + unexpected_type @ _ => { + let unexpected_type = if let Some(pos) = unexpected_type.find(':') { + &unexpected_type[pos + 1..] + } else { + unexpected_type + }; + + return Err(Error::unexpected_component(&unexpected_type, #expected_types)); + } + } + } + + Ok(#widget_content_struct_name { + #(#prop_union_results)* + #(#prop_other_results)* + #(#per_type_prop_results)* + #ordered_members_prop_result + #text_prop_result + }) + } + }); + } + } + Component::Root { + children, shared_types, .. + } => { + let children_names = children.iter().map(|item| &item.component_name).join(", "); + + let match_arm: Vec<_> = children + .iter() + .map(|component| { + let name = &component.component_name.to_string(); + let name_ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); + let internal_name = &component.component_internal_name; + let method_name = format!("deserialize_{}_widget", internal_name); + let method_name = syn::Ident::new(&method_name, proc_macro2::Span::call_site()); + + let id = format!("gauntlet:{}", internal_name); + + quote! { + #id => { + let widget = #method_name(scope, container)?; + + RootWidgetMembers::#name_ident(widget) + } + } + }) + .collect(); + + output_components.push(quote! { + pub fn deserialize_root_widget( + scope: &mut v8::HandleScope, + container: v8::Local, + ) -> Result { + let container: v8::Local = container.try_into() + .map_err(|_| error_internal!("root container is not an object"))?; + + let content = extract_object_value(scope, container, "content") + .ok_or(error_internal!("'content' key is not present on root object"))?; + + let content: v8::Local = content.try_into() + .map_err(|_| error_internal!("'content' in root object is not an array"))?; + + let content = match content.length() { + 0 => None, + 1 => { + let container = content + .get_index(scope, 0) + .ok_or(error_internal!("unable to get item from root content array at index {}", 0))?; + + let container: v8::Local = container + .try_into() + .map_err(|_| error_internal!("item in root content array at index {} is not an object, got: '{}'", 0, container.type_repr()))?; + + let members = deserialize_root_widget_members(scope, container)?; + + Some(members) + } + _ => { + return Err(error_internal!( + "root component can only contain a single child, got: '{}'", + content.length() + )); + } + }; + + Ok(RootWidget { content }) + } + + fn deserialize_root_widget_members( + scope: &mut v8::HandleScope, + container: v8::Local, + ) -> Result { + let widget_type = deserialize_widget_type(scope, container)?; + + let members = match widget_type.as_str() { + #(#match_arm)* + _ => { + return Err(Error::unexpected_component(&widget_type, #children_names)); + } + }; + + Ok(members) + } + }); + + let shared_types = shared_types + .iter() + .map(|(type_name, shared_type)| { + let method_name = format!("deserialize_shared_type_{}", type_name.to_case(Case::Snake)); + let method_name = syn::Ident::new(&method_name, proc_macro2::Span::call_site()); + let type_name_ident = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); + + match shared_type { + SharedType::Enum { .. } => { + quote! { + fn #method_name(scope: &mut v8::HandleScope, value: v8::Local) -> Result<#type_name_ident> { + let value: v8::Local = value.try_into() + .map_err(|_| Error::unexpected_type(value.type_repr(), "enum (string)"))?; + + let value = value.to_rust_string_lossy(scope); + + let value = <#type_name_ident as std::str::FromStr>::from_str(&value) + .map_err(|_| Error::invalid_enum_value(#type_name, &value))?; + + Ok(value) + } + } + } + SharedType::Object { items } => { + let field_names: Vec<_> = items.iter() + .map(|(name, _)| { + let name = syn::Ident::new(&name, proc_macro2::Span::call_site()); + quote! { + #name, + } + }) + .collect(); + + let fields: Vec<_> = items.iter() + .map(|(name, property_type)| { + let name_ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); + + let deserialize_method = match &property_type { + PropertyType::String => { + quote! { + deserialize_string(scope, #name_ident)? + } + } + PropertyType::Number => { + quote! { + deserialize_number(#name_ident)? + } + } + PropertyType::Boolean => { + quote! { + deserialize_boolean(#name_ident)? + } + } + _ => { + todo!() + } + }; + + quote! { + let #name_ident = extract_object_value(scope, value, #name) + .ok_or(Error::required_prop(#name))?; + + let #name_ident = #deserialize_method; + } + }) + .collect(); + + quote! { + fn #method_name(scope: &mut v8::HandleScope, value: v8::Local) -> Result<#type_name_ident> { + let value: v8::Local = value + .try_into() + .map_err(|_| Error::unexpected_type(value.type_repr(), "object"))?; + + #(#fields)* + + Ok(#type_name_ident { + #(#field_names)* + }) + } + } + } + SharedType::Union { items } => { + let deserializers: Vec<_> = items.iter() + .enumerate() + .map(|(index, property_type)| { + let name = match property_type { + PropertyType::SharedTypeRef { name } => name, + _ => todo!() + }; + let name_ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); + + let method_name = format!("deserialize_shared_type_{}", name.to_case(Case::Snake)); + let method_name = syn::Ident::new(&method_name, proc_macro2::Span::call_site()); + + if index == 0 { + quote! { + let result = #method_name(scope, value).map(#type_name_ident::#name_ident) + } + } else { + quote! { + .or_else(|_| #method_name(scope, value).map(#type_name_ident::#name_ident)); + } + } + }) + .collect(); + + quote! { + fn #method_name(scope: &mut v8::HandleScope, value: v8::Local) -> Result<#type_name_ident> { + #(#deserializers)* + + Ok(result?) + } + } + } + } + }) + .collect(); + + output_components.push(shared_types); + } + Component::TextPart { .. } => {} + } + } + + let result = quote! { + #(#output_components)* + }; + + result.into() +} diff --git a/rust/utils_macros/src/widget_model_gen.rs b/rust/utils_macros/src/widget_model_gen.rs new file mode 100644 index 0000000..35d9a37 --- /dev/null +++ b/rust/utils_macros/src/widget_model_gen.rs @@ -0,0 +1,470 @@ +use std::ops::Deref; + +use convert_case::Case; +use convert_case::Casing; +use gauntlet_component_model::Arity; +use gauntlet_component_model::Children; +use gauntlet_component_model::Component; +use gauntlet_component_model::ComponentName; +use gauntlet_component_model::Property; +use gauntlet_component_model::PropertyKind; +use gauntlet_component_model::PropertyType; +use gauntlet_component_model::SharedType; +use gauntlet_component_model::create_component_model; +use indexmap::IndexMap; +use itertools::Itertools; +use proc_macro::TokenStream; +use quote::ToTokens; +use quote::quote; + +pub fn widget_model_gen() -> TokenStream { + let components = create_component_model(); + + let mut output_components = vec![]; + + for component in &components { + match component { + Component::Standard { + name, props, children, .. + } => { + let props_has_content = props + .iter() + .any(|prop| matches!(prop.property_type.kind(), PropertyKind::Component)); + + let children_has_content = match children { + Children::Members { + ordered_members, + per_type_members, + .. + } + | Children::StringOrMembers { + ordered_members, + per_type_members, + .. + } => !ordered_members.is_empty() || !per_type_members.is_empty(), + _ => false, + }; + + let has_text = matches!(children, Children::StringOrMembers { .. } | Children::String { .. }); + + let has_content = children_has_content || props_has_content || has_text; + + let default = IndexMap::new(); + + let (ordered_members, per_type_members) = match children { + Children::Members { + ordered_members, + per_type_members, + .. + } + | Children::StringOrMembers { + ordered_members, + per_type_members, + .. + } => (ordered_members, per_type_members), + _ => (&default, &default), + }; + + if !ordered_members.is_empty() { + let component_refs = ordered_members + .iter() + .map(|(_member_name, component_ref)| component_ref) + .unique_by(|component_ref| component_ref.component_name.clone()) + .map(|component_ref| { + let enum_item_name = component_ref.component_name.to_string(); + let enum_item_name = syn::Ident::new(&enum_item_name, proc_macro2::Span::call_site()); + + let widget_name = format!("{}Widget", component_ref.component_name); + let widget_name = syn::Ident::new(&widget_name, proc_macro2::Span::call_site()); + + quote! { + #enum_item_name(#widget_name) + } + }) + .collect::>(); + + let enum_name = &format!("{}WidgetOrderedMembers", name); + let enum_name = syn::Ident::new(enum_name, proc_macro2::Span::call_site()); + + output_components.push(quote! { + #[derive(Debug, Encode, Decode)] + pub enum #enum_name { + #(#component_refs),* + } + }) + } + + if has_content { + { + let props: Vec<_> = props + .iter() + .map(|prop| { + if matches!(prop.property_type.kind(), PropertyKind::Component) { + let is_union = match &prop.property_type { + PropertyType::Union { .. } => true, + PropertyType::Array { item } => { + matches!(item.as_ref(), PropertyType::Union { .. }) + } + _ => false, + }; + + if is_union { + let field_name = prop.name.to_case(Case::Snake); + let field_name = syn::Ident::new(&field_name, proc_macro2::Span::call_site()); + + let type_name = generate_required_type( + &prop.property_type, + Some(format!("{}{}", name, &prop.name.to_case(Case::Pascal))), + ); + + Some(quote! { + pub #field_name: #type_name, + }) + } else { + let field_name = prop.name.to_case(Case::Snake); + let field_name = syn::Ident::new(&field_name, proc_macro2::Span::call_site()); + + let type_name = generate_type(&prop, name); + + Some(quote! { + pub #field_name: #type_name, + }) + } + } else { + None + } + }) + .collect(); + + let per_type_members: Vec<_> = per_type_members + .iter() + .map(|(member_name, component_ref)| { + match component_ref.arity { + Arity::ZeroOrOne => { + let field_name = member_name.to_case(Case::Snake); + let field_name = syn::Ident::new(&field_name, proc_macro2::Span::call_site()); + + let widget_name = &format!("{}Widget", component_ref.component_name); + let widget_name = syn::Ident::new(&widget_name, proc_macro2::Span::call_site()); + + quote! { + pub #field_name: Option<#widget_name>, + } + } + Arity::One => { + let field_name = member_name.to_case(Case::Snake); + let field_name = syn::Ident::new(&field_name, proc_macro2::Span::call_site()); + + let widget_name = &format!("{}Widget", component_ref.component_name); + let widget_name = syn::Ident::new(&widget_name, proc_macro2::Span::call_site()); + + quote! { + pub #field_name: #widget_name, + } + } + Arity::ZeroOrMore => { + todo!() + } + } + }) + .collect(); + + let ordered_members = if !ordered_members.is_empty() { + let ordered_members_name = &format!("{}WidgetOrderedMembers", name); + let ordered_members_name = + syn::Ident::new(&ordered_members_name, proc_macro2::Span::call_site()); + + Some(quote! { + pub ordered_members: Vec<#ordered_members_name>, + }) + } else { + None + }; + + let text = if has_text { + Some(quote! { + pub text: Vec, + }) + } else { + None + }; + + let widget_content_name = &format!("{}WidgetContent", name); + let widget_content_name = syn::Ident::new(&widget_content_name, proc_macro2::Span::call_site()); + + output_components.push(quote! { + #[derive(Debug, Encode, Decode)] + pub struct #widget_content_name { + #(#props)* + #(#per_type_members)* + #ordered_members + #text + } + }) + } + } + + let prop_fields: Vec<_> = props + .iter() + .filter(|prop| matches!(prop.property_type.kind(), PropertyKind::Property)) + .map(|prop| { + let field_name = prop.name.to_case(Case::Snake); + let field_name = syn::Ident::new(&field_name, proc_macro2::Span::call_site()); + + let type_name = generate_type(&prop, name); + + quote! { + pub #field_name: #type_name, + } + }) + .collect(); + + let content = if has_content { + let widget_content_name = &format!("{}WidgetContent", name); + let widget_content_name = syn::Ident::new(&widget_content_name, proc_macro2::Span::call_site()); + + Some(quote! { + pub content: #widget_content_name, + }) + } else { + None + }; + + let widget_name = &format!("{}Widget", name); + let widget_name = syn::Ident::new(&widget_name, proc_macro2::Span::call_site()); + + output_components.push(quote! { + #[derive(Debug, Encode, Decode)] + pub struct #widget_name { + pub __id__: UiWidgetId, + #(#prop_fields)* + #content + } + }); + + fn generate_union( + name: &ComponentName, + items: &Vec, + prop_name: &String, + ) -> proc_macro2::TokenStream { + let enum_items: Vec<_> = items + .iter() + .enumerate() + .map(|(index, property_type)| { + let item_name = format!("_{}", index); + let item_name = syn::Ident::new(&item_name, proc_macro2::Span::call_site()); + let item_type = generate_required_type(&property_type, None); + quote! { + #item_name(#item_type) + } + }) + .collect(); + + let enum_name = format!("{}{}", name, prop_name.to_case(Case::Pascal)); + let enum_name = syn::Ident::new(&enum_name, proc_macro2::Span::call_site()); + + quote! { + #[derive(Debug, Encode, Decode)] + pub enum #enum_name { + #(#enum_items),* + } + } + } + + let unions: Vec<_> = props + .iter() + .map(|prop| { + match &prop.property_type { + PropertyType::Union { items } => Some(generate_union(&name, &items, &prop.name)), + PropertyType::Array { item } => { + match item.deref() { + PropertyType::Union { items } => Some(generate_union(&name, &items, &prop.name)), + _ => None, + } + } + _ => None, + } + }) + .collect(); + + output_components.push(quote! { + #(#unions)* + }) + } + Component::Root { + children, shared_types, .. + } => { + let shared_types = shared_types + .iter() + .map(|(type_name, shared_type)| { + match shared_type { + SharedType::Enum { items } => { + let items: Vec<_> = items + .iter() + .map(|item| { + let item_ident = syn::Ident::new(&item, proc_macro2::Span::call_site()); + quote! { + #item_ident + } + }) + .collect(); + + let type_name = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); + + quote! { + #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, strum::EnumString)] + pub enum #type_name { + #(#items),* + } + } + } + SharedType::Object { items } => { + let items: Vec<_> = items + .iter() + .map(|(property_name, property_type)| { + let field_name = property_name; + let field_name = syn::Ident::new(&field_name, proc_macro2::Span::call_site()); + + let field_type = generate_required_type( + &property_type, + Some(format!("{}{}", type_name, property_name)), + ); + quote! { + pub #field_name: #field_type + } + }) + .collect(); + + let type_name = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); + + quote! { + #[derive(Debug, Encode, Decode)] + pub struct #type_name { + #(#items),* + } + } + } + SharedType::Union { items } => { + let type_name = syn::Ident::new(&type_name, proc_macro2::Span::call_site()); + + let items: Vec<_> = items + .iter() + .map(|property_type| { + match property_type { + PropertyType::SharedTypeRef { name } => { + let enum_item = name; + let enum_item = + syn::Ident::new(&enum_item, proc_macro2::Span::call_site()); + + quote! { + #enum_item(#enum_item) + } + } + _ => { + todo!() + } + } + }) + .collect(); + + quote! { + #[derive(Debug, Encode, Decode)] + pub enum #type_name { + #(#items),* + } + } + } + } + }) + .collect(); + + output_components.push(shared_types); + + let children: Vec<_> = children + .iter() + .map(|component_ref| { + let enum_item = &component_ref.component_name; + let enum_item = syn::Ident::new(&enum_item.to_string(), proc_macro2::Span::call_site()); + + let enum_item_type = format!("{}Widget", component_ref.component_name); + let enum_item_type = syn::Ident::new(&enum_item_type, proc_macro2::Span::call_site()); + + quote! { + #enum_item(#enum_item_type) + } + }) + .collect(); + + output_components.push(quote! { + #[derive(Debug, Encode, Decode)] + pub enum RootWidgetMembers { + #(#children),* + } + }); + + output_components.push(quote! { + #[derive(Debug, Encode, Decode)] + pub struct RootWidget { + pub content: Option + } + }) + } + Component::TextPart { .. } => {} + } + } + + let result = quote! { + #(#output_components)* + }; + + result.into() +} + +fn generate_type(property: &Property, name: &ComponentName) -> proc_macro2::TokenStream { + match property.optional { + true => { + generate_optional_type( + &property.property_type, + format!("{}{}", name, &property.name.to_case(Case::Pascal)), + ) + } + false => { + generate_required_type( + &property.property_type, + Some(format!("{}{}", name, &property.name.to_case(Case::Pascal))), + ) + } + } +} + +fn generate_optional_type(property_type: &PropertyType, union_name: String) -> proc_macro2::TokenStream { + let inner_type = generate_required_type(property_type, Some(union_name)); + quote!(Option<#inner_type>) +} + +fn generate_required_type(property_type: &PropertyType, union_name: Option) -> proc_macro2::TokenStream { + // let enum_item_type = syn::Ident::new(&enum_item_type, proc_macro2::Span::call_site()); + match property_type { + PropertyType::String => syn::Ident::new("String", proc_macro2::Span::call_site()).into_token_stream(), + PropertyType::Number => syn::Ident::new("f64", proc_macro2::Span::call_site()).into_token_stream(), + PropertyType::Boolean => syn::Ident::new("bool", proc_macro2::Span::call_site()).into_token_stream(), + PropertyType::Function { .. } => panic!("client doesn't know about functions in properties"), + PropertyType::Component { reference } => { + let name = format!("{}Widget", reference.component_name.to_string()); + syn::Ident::new(&name, proc_macro2::Span::call_site()).into_token_stream() + } + PropertyType::SharedTypeRef { name } => { + syn::Ident::new(name, proc_macro2::Span::call_site()).into_token_stream() + } + PropertyType::Union { .. } => { + match union_name { + None => panic!("should not be used"), + Some(union_name) => syn::Ident::new(&union_name, proc_macro2::Span::call_site()).into_token_stream(), + } + } + PropertyType::Array { item } => { + let inner_type = generate_required_type(item, union_name); + quote!(Vec<#inner_type>) + } + } +} From e1f2ca85ec82bf68bef78b007a5c234d0d7317e5 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Mon, 21 Jul 2025 21:32:43 +0200 Subject: [PATCH 63/91] Update winit fork, fix fractional scaling for layershell --- Cargo.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e7e3e2..4a0eb30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5762,7 +5762,7 @@ dependencies = [ [[package]] name = "iced" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "iced_core", "iced_debug", @@ -5778,7 +5778,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "bitflags 2.9.1", "bytes", @@ -5796,7 +5796,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "iced_core", "iced_futures", @@ -5827,7 +5827,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "futures", "iced_core", @@ -5841,7 +5841,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "bitflags 2.9.1", "bytemuck", @@ -5861,7 +5861,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "iced_graphics", "iced_runtime", @@ -5870,7 +5870,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -5882,7 +5882,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "bytes", "iced_core", @@ -5895,7 +5895,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "bytemuck", "cosmic-text", @@ -5912,7 +5912,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "bitflags 2.9.1", "bytemuck", @@ -5932,7 +5932,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "iced_renderer", "iced_runtime", @@ -5947,7 +5947,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#ae9d84a81faff6d8d972ab47bb173eaf2133df7a" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" dependencies = [ "iced_debug", "iced_program", @@ -13292,7 +13292,7 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winit" version = "0.30.99" -source = "git+https://github.com/project-gauntlet/winit.git?rev=3f32c4232443a9572e6c96b5fcbb1fd9ad636f89#3f32c4232443a9572e6c96b5fcbb1fd9ad636f89" +source = "git+https://github.com/project-gauntlet/winit.git?rev=838583241fcd5bd855bd140a4df6fce3266a11a6#838583241fcd5bd855bd140a4df6fce3266a11a6" dependencies = [ "ahash 0.8.12", "android-activity", From 72123db351fd0c3a52b361166ed3cc225051a2d2 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 24 Jul 2025 20:14:23 +0200 Subject: [PATCH 64/91] Refine nullability of event arguments. Distinguish between null and undefined properties when parsing react component tree --- dev_plugin/src/test-list-focus.tsx | 2 +- js/api/src/gen/components.tsx | 28 +-- js/api_build/src/index.ts | 48 ++-- js/core/src/render.tsx | 3 + js/typings/index.d.ts | 5 +- rust/client/build.rs | 76 ++++-- rust/client/src/ui/widget/events.rs | 8 +- rust/common/src/model.rs | 8 + rust/common_plugin_runtime/src/model.rs | 1 + rust/component_model/src/lib.rs | 236 ++++++++++++------ rust/server/src/plugins/js.rs | 1 + .../src/widget_deserialization_gen.rs | 57 +++-- rust/utils_macros/src/widget_model_gen.rs | 26 +- 13 files changed, 335 insertions(+), 164 deletions(-) diff --git a/dev_plugin/src/test-list-focus.tsx b/dev_plugin/src/test-list-focus.tsx index 5534ba2..f83ba51 100644 --- a/dev_plugin/src/test-list-focus.tsx +++ b/dev_plugin/src/test-list-focus.tsx @@ -2,7 +2,7 @@ import { ReactElement, useState } from "react"; import { List } from "@project-gauntlet/api/components"; export default function Main(): ReactElement { - const [id, setId] = useState(undefined); + const [id, setId] = useState(null); return ( diff --git a/js/api/src/gen/components.tsx b/js/api/src/gen/components.tsx index 315642f..bea973c 100644 --- a/js/api/src/gen/components.tsx +++ b/js/api/src/gen/components.tsx @@ -6,7 +6,7 @@ declare global { ["gauntlet:action"]: { id?: string; label: string; - onAction: (id: string | undefined) => void; + onAction: (id: string | null) => void; }; ["gauntlet:action_panel_section"]: { children?: ElementComponent; @@ -82,12 +82,12 @@ declare global { ["gauntlet:text_field"]: { label?: string; value?: string; - onChange?: (value: string | undefined) => void; + onChange?: (value: string) => void; }; ["gauntlet:password_field"]: { label?: string; value?: string; - onChange?: (value: string | undefined) => void; + onChange?: (value: string) => void; }; ["gauntlet:checkbox"]: { label?: string; @@ -103,7 +103,7 @@ declare global { children?: ElementComponent; label?: string; value?: string; - onChange?: (value: string | undefined) => void; + onChange?: (value: string) => void; }; ["gauntlet:separator"]: {}; ["gauntlet:form"]: { @@ -133,7 +133,7 @@ declare global { ["gauntlet:search_bar"]: { value?: string; placeholder?: string; - onChange?: (value: string | undefined) => void; + onChange?: (value: string) => void; }; ["gauntlet:list_item"]: { children?: ElementComponent; @@ -150,7 +150,7 @@ declare global { ["gauntlet:list"]: { children?: ElementComponent; isLoading?: boolean; - onItemFocusChange?: (itemId: string | undefined) => void; + onItemFocusChange?: (itemId: string | null) => void; }; ["gauntlet:grid_item"]: { children?: ElementComponent; @@ -168,7 +168,7 @@ declare global { children?: ElementComponent; isLoading?: boolean; columns?: number; - onItemFocusChange?: (itemId: string | undefined) => void; + onItemFocusChange?: (itemId: string | null) => void; }; } } @@ -364,7 +364,7 @@ export type ImageLike = DataSource | Icons; export interface ActionProps { id?: string; label: string; - onAction: (id: string | undefined) => void; + onAction: (id: string | null) => void; } export const Action: FC = (props: ActionProps): ReactNode => { return ; @@ -558,7 +558,7 @@ Detail.Content = Content; export interface TextFieldProps { label?: string; value?: string; - onChange?: (value: string | undefined) => void; + onChange?: (value: string) => void; } export const TextField: FC = (props: TextFieldProps): ReactNode => { return ; @@ -566,7 +566,7 @@ export const TextField: FC = (props: TextFieldProps): ReactNode export interface PasswordFieldProps { label?: string; value?: string; - onChange?: (value: string | undefined) => void; + onChange?: (value: string) => void; } export const PasswordField: FC = (props: PasswordFieldProps): ReactNode => { return ; @@ -591,7 +591,7 @@ export interface SelectProps { children?: ElementComponent; label?: string; value?: string; - onChange?: (value: string | undefined) => void; + onChange?: (value: string) => void; } export const Select: FC & { Item: typeof SelectItem; @@ -669,7 +669,7 @@ export const TextAccessory: FC = (props: TextAccessoryProps) export interface SearchBarProps { value?: string; placeholder?: string; - onChange?: (value: string | undefined) => void; + onChange?: (value: string) => void; } export const SearchBar: FC = (props: SearchBarProps): ReactNode => { return ; @@ -699,7 +699,7 @@ export interface ListProps { children?: ElementComponent; actions?: ElementComponent; isLoading?: boolean; - onItemFocusChange?: (itemId: string | undefined) => void; + onItemFocusChange?: (itemId: string | null) => void; } export const List: FC & { Item: typeof ListItem; @@ -745,7 +745,7 @@ export interface GridProps { isLoading?: boolean; actions?: ElementComponent; columns?: number; - onItemFocusChange?: (itemId: string | undefined) => void; + onItemFocusChange?: (itemId: string | null) => void; } export const Grid: FC & { Item: typeof GridItem; diff --git a/js/api_build/src/index.ts b/js/api_build/src/index.ts index 615fd16..dae197c 100644 --- a/js/api_build/src/index.ts +++ b/js/api_build/src/index.ts @@ -270,7 +270,7 @@ function makeComponents(modelInput: Component[]): ts.SourceFile { undefined, ts.factory.createIdentifier(propName), undefined, - makeType(type) + makeType(type, "no") ) }) ) @@ -285,7 +285,7 @@ function makeComponents(modelInput: Component[]): ts.SourceFile { ts.factory.createIdentifier(name), undefined, ts.factory.createUnionTypeNode( - sharedType.items.map(type => makeType(type)) + sharedType.items.map(type => makeType(type, "no")) ) ) @@ -541,8 +541,8 @@ function makePropertyTypes(component: StandardComponent, componentPropsInChildre return ts.factory.createPropertySignature( undefined, ts.factory.createIdentifier(property.name), - !property.optional ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken), - makeType(property.type) + property.optional == "no" ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken), + makeType(property.type, property.optional) ) }); @@ -633,37 +633,46 @@ function makeChildrenType(type: Children, additionalComponentRefs: ComponentRef[ } -function makeType(type: PropertyType): ts.TypeNode { +function makeType(type: PropertyType, optional: Property["optional"]): ts.TypeNode { + let result: ts.TypeNode switch (type.type) { case "boolean": { - return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword) + result = ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword) + break; } case "number": { - return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) + result = ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) + break; } case "string": { - return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + result = ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + break; } case "function": { const params = type.arguments.map(arg => { + if (arg.optional != "no" && arg.optional != "yes") { + throw new Error("following optional type is not supported here: " + arg.optional) + } + return ts.factory.createParameterDeclaration( undefined, undefined, ts.factory.createIdentifier(arg.name), undefined, - !arg.optional ? makeType(arg.type) : ts.factory.createUnionTypeNode([makeType(arg.type), ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)]), + arg.optional == "no" ? makeType(arg.type, "no") : ts.factory.createUnionTypeNode([makeType(arg.type, arg.optional), ts.factory.createLiteralTypeNode(ts.factory.createNull())]), undefined ) }); - return ts.factory.createFunctionTypeNode( + result = ts.factory.createFunctionTypeNode( undefined, params, ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword) ) + break; } case "component": { - return ts.factory.createTypeReferenceNode( + result = ts.factory.createTypeReferenceNode( ts.factory.createIdentifier("ElementComponent"), [ ts.factory.createTypeQueryNode( @@ -672,24 +681,33 @@ function makeType(type: PropertyType): ts.TypeNode { ) ] ) + break; } case "array": { - return ts.factory.createArrayTypeNode(makeType(type.item)) + result = ts.factory.createArrayTypeNode(makeType(type.item, "no")) + break; } case "shared_type_ref": { - return ts.factory.createTypeReferenceNode( + result = ts.factory.createTypeReferenceNode( ts.factory.createIdentifier(type.name), undefined ) + break; } case "union": { - return ts.factory.createUnionTypeNode(type.items.map(value => makeType(value))) + result = ts.factory.createUnionTypeNode(type.items.map(value => makeType(value, "no"))) + break } default: { throw new Error(`unsupported type ${JSON.stringify(type)}`) } } + if (optional == "yes_but_complicated") { + return ts.factory.createUnionTypeNode([result, ts.factory.createLiteralTypeNode(ts.factory.createNull())]); + } else { + return result + } } function isInProperty(propertyType: PropertyType) { @@ -767,4 +785,4 @@ if (!existsSync(genDir)) { mkdirSync(genDir); } -generate("./component_model.json", `${genDir}/components.tsx`) \ No newline at end of file +generate("./component_model.json", `${genDir}/components.tsx`) diff --git a/js/core/src/render.tsx b/js/core/src/render.tsx index 2a7bb97..dd91ec6 100644 --- a/js/core/src/render.tsx +++ b/js/core/src/render.tsx @@ -91,6 +91,9 @@ export function handleEvent(event: ViewEvent) { case "Undefined": { return undefined } + case "Null": { + return null + } case "String": { return arg.value } diff --git a/js/typings/index.d.ts b/js/typings/index.d.ts index c34dfa6..519b2cc 100644 --- a/js/typings/index.d.ts +++ b/js/typings/index.d.ts @@ -98,11 +98,12 @@ type RefreshSearchIndex = { type: "RefreshSearchIndex" } -type PropertyValue = PropertyValueString | PropertyValueNumber | PropertyValueBool | PropertyValueUndefined +type PropertyValue = PropertyValueString | PropertyValueNumber | PropertyValueBool | PropertyValueUndefined | PropertyValueNull type PropertyValueString = { type: "String", value: string } type PropertyValueNumber = { type: "Number", value: number } type PropertyValueBool = { type: "Bool", value: boolean } type PropertyValueUndefined = { type: "Undefined" } +type PropertyValueNull = { type: "Null" } type UiWidget = { widgetId: number, @@ -312,7 +313,7 @@ type TextPartComponent = { type Property = { name: string - optional: boolean + optional: "no" | "yes" | "yes_but_complicated" type: PropertyType } type Children = ChildrenMembers | ChildrenString | ChildrenNone | ChildrenStringOrMembers diff --git a/rust/client/build.rs b/rust/client/build.rs index f14f36b..eb9de1e 100644 --- a/rust/client/build.rs +++ b/rust/client/build.rs @@ -7,6 +7,7 @@ use convert_case::Case; use convert_case::Casing; use gauntlet_component_model::Component; use gauntlet_component_model::ComponentName; +use gauntlet_component_model::OptionalKind; use gauntlet_component_model::Property; use gauntlet_component_model::PropertyType; use gauntlet_component_model::create_component_model; @@ -48,33 +49,51 @@ fn main() -> anyhow::Result<()> { for arg in arguments { match arg.property_type { PropertyType::String => { - if arg.optional { - output.push_str(&format!(" {}.map(|#[allow(non_snake_case)] {}| gauntlet_common::model::UiPropertyValue::String({})).unwrap_or_else(|| gauntlet_common::model::UiPropertyValue::Undefined),\n", arg.name, arg.name, arg.name)); - } else { - output.push_str(&format!( - " gauntlet_common::model::UiPropertyValue::String({}),\n", - arg.name - )); + match arg.optional { + OptionalKind::No => { + output.push_str(&format!( + " gauntlet_common::model::UiPropertyValue::String({}),\n", + arg.name + )); + } + OptionalKind::Yes => { + output.push_str(&format!(" {}.map(|#[allow(non_snake_case)] {}| gauntlet_common::model::UiPropertyValue::String({})).unwrap_or_else(|| gauntlet_common::model::UiPropertyValue::Null),\n", arg.name, arg.name, arg.name)); + } + OptionalKind::YesButComplicated => { + todo!() + } } } PropertyType::Number => { - if arg.optional { - output.push_str(&format!(" {}.map(|{}| gauntlet_common::model::UiPropertyValue::Number({})).unwrap_or_else(|| gauntlet_common::model::UiPropertyValue::Undefined),\n", arg.name, arg.name, arg.name)); - } else { - output.push_str(&format!( - " gauntlet_common::model::UiPropertyValue::Number({}),\n", - arg.name - )); + match arg.optional { + OptionalKind::No => { + output.push_str(&format!( + " gauntlet_common::model::UiPropertyValue::Number({}),\n", + arg.name + )); + } + OptionalKind::Yes => { + output.push_str(&format!(" {}.map(|{}| gauntlet_common::model::UiPropertyValue::Number({})).unwrap_or_else(|| gauntlet_common::model::UiPropertyValue::Null),\n", arg.name, arg.name, arg.name)); + } + OptionalKind::YesButComplicated => { + todo!() + } } } PropertyType::Boolean => { - if arg.optional { - output.push_str(&format!(" {}.map(|{}| gauntlet_common::model::UiPropertyValue::Bool({})).unwrap_or_else(|| gauntlet_common::model::UiPropertyValue::Undefined),\n", arg.name, arg.name, arg.name)); - } else { - output.push_str(&format!( - " gauntlet_common::model::UiPropertyValue::Bool({}),\n", - arg.name - )); + match arg.optional { + OptionalKind::No => { + output.push_str(&format!( + " gauntlet_common::model::UiPropertyValue::Bool({}),\n", + arg.name + )); + } + OptionalKind::Yes => { + output.push_str(&format!(" {}.map(|{}| gauntlet_common::model::UiPropertyValue::Bool({})).unwrap_or_else(|| gauntlet_common::model::UiPropertyValue::Null),\n", arg.name, arg.name, arg.name)); + } + OptionalKind::YesButComplicated => { + todo!() + } } } _ => { @@ -105,17 +124,20 @@ fn generate_file>(path: P, text: &str) -> std::io::Result<()> { fn generate_type(property: &Property, name: &ComponentName) -> String { match property.optional { - true => { + OptionalKind::No => { + generate_required_type( + &property.property_type, + Some(format!("{}{}", name, &property.name.to_case(Case::Pascal))), + ) + } + OptionalKind::Yes => { generate_optional_type( &property.property_type, format!("{}{}", name, &property.name.to_case(Case::Pascal)), ) } - false => { - generate_required_type( - &property.property_type, - Some(format!("{}{}", name, &property.name.to_case(Case::Pascal))), - ) + OptionalKind::YesButComplicated => { + todo!() } } } diff --git a/rust/client/src/ui/widget/events.rs b/rust/client/src/ui/widget/events.rs index 071194e..67642c1 100644 --- a/rust/client/src/ui/widget/events.rs +++ b/rust/client/src/ui/widget/events.rs @@ -98,7 +98,7 @@ impl ComponentWidgetEvent { *state_value = Some(value.clone()); - Some(create_select_on_change_event(widget_id, Some(value))) + Some(create_select_on_change_event(widget_id, value)) } ComponentWidgetEvent::OnChangeTextField { widget_id, value } => { let Some(state) = state else { @@ -111,7 +111,7 @@ impl ComponentWidgetEvent { *state_value = value.clone(); - Some(create_text_field_on_change_event(widget_id, Some(value))) + Some(create_text_field_on_change_event(widget_id, value)) } ComponentWidgetEvent::OnChangePasswordField { widget_id, value } => { let Some(state) = state else { @@ -124,7 +124,7 @@ impl ComponentWidgetEvent { *state_value = value.clone(); - Some(create_password_field_on_change_event(widget_id, Some(value))) + Some(create_password_field_on_change_event(widget_id, value)) } ComponentWidgetEvent::OnChangeSearchBar { widget_id, value } => { let Some(state) = state else { @@ -137,7 +137,7 @@ impl ComponentWidgetEvent { *state_value = value.clone(); - Some(create_search_bar_on_change_event(widget_id, Some(value))) + Some(create_search_bar_on_change_event(widget_id, value)) } ComponentWidgetEvent::ToggleActionPanel { .. } => { Some(UiViewEvent::AppEvent { diff --git a/rust/common/src/model.rs b/rust/common/src/model.rs index a988dad..d706f35 100644 --- a/rust/common/src/model.rs +++ b/rust/common/src/model.rs @@ -233,6 +233,13 @@ pub enum KeyboardEventOrigin { gauntlet_utils_macros::widget_model_gen!(); +#[derive(Debug, Encode, Decode)] +pub enum JsOption { + Undefined, + Null, + Value(T), +} + // TODO generate this #[allow(async_fn_in_trait)] pub trait WidgetVisitor { @@ -476,6 +483,7 @@ pub enum UiPropertyValue { Bytes(bytes::Bytes), Array(Vec), Object(HashMap), + Null, Undefined, } diff --git a/rust/common_plugin_runtime/src/model.rs b/rust/common_plugin_runtime/src/model.rs index 46f69c1..f267401 100644 --- a/rust/common_plugin_runtime/src/model.rs +++ b/rust/common_plugin_runtime/src/model.rs @@ -74,6 +74,7 @@ pub enum JsUiPropertyValue { Number { value: f64 }, Bool { value: bool }, Undefined, + Null, } #[derive(Debug, Encode, Decode)] diff --git a/rust/component_model/src/lib.rs b/rust/component_model/src/lib.rs index e65fb7e..8210a4e 100644 --- a/rust/component_model/src/lib.rs +++ b/rust/component_model/src/lib.rs @@ -61,11 +61,21 @@ pub enum Component { pub struct Property { pub name: String, pub description: String, - pub optional: bool, + pub optional: OptionalKind, #[serde(rename = "type")] pub property_type: PropertyType, } +#[derive(Debug, Clone, Serialize)] +pub enum OptionalKind { + #[serde(rename = "no")] + No, + #[serde(rename = "yes")] + Yes, + #[serde(rename = "yes_but_complicated")] + YesButComplicated, // distinguish between null and undefined +} + #[derive(Debug, Clone, Serialize)] #[serde(tag = "type")] pub enum PropertyType { @@ -275,7 +285,7 @@ fn text_part() -> Component { props: vec![Property { name: "value".into(), description: "".to_string(), - optional: false, + optional: OptionalKind::No, property_type: PropertyType::String, }], } @@ -594,7 +604,7 @@ fn root(children: &[&Component]) -> Component { } } -fn event(name: impl Into, description: String, optional: bool, arguments: I) -> Property +fn event(name: impl Into, description: String, optional: OptionalKind, arguments: I) -> Property where I: IntoIterator, { @@ -608,7 +618,12 @@ where } } -fn property(name: impl Into, description: String, optional: bool, property_type: PropertyType) -> Property { +fn property( + name: impl Into, + description: String, + optional: OptionalKind, + property_type: PropertyType, +) -> Property { Property { name: name.into(), description, @@ -623,18 +638,23 @@ pub fn create_component_model() -> Vec { mark_doc!("/action/description.md"), "Action", [ - property("id", mark_doc!("/action/props/id.md"), true, PropertyType::String), + property( + "id", + mark_doc!("/action/props/id.md"), + OptionalKind::Yes, + PropertyType::String, + ), property( "label", mark_doc!("/action/props/label.md"), - false, + OptionalKind::No, PropertyType::String, ), event( "onAction", mark_doc!("/action/props/onAction.md"), - false, - [property("id", "".to_string(), true, PropertyType::String)], + OptionalKind::No, + [property("id", "".to_string(), OptionalKind::Yes, PropertyType::String)], ), ], children_none(), @@ -647,7 +667,7 @@ pub fn create_component_model() -> Vec { [property( "title", mark_doc!("/action_panel_section/props/title.md"), - true, + OptionalKind::Yes, PropertyType::String, )], children_members([member("Action", &action_component, Arity::ZeroOrMore)], []), @@ -660,7 +680,7 @@ pub fn create_component_model() -> Vec { [property( "title", mark_doc!("/action_panel/props/title.md"), - true, + OptionalKind::Yes, PropertyType::String, )], children_members( @@ -680,13 +700,13 @@ pub fn create_component_model() -> Vec { property( "label", mark_doc!("/metadata_link/props/label.md"), - false, + OptionalKind::No, PropertyType::String, ), property( "href", mark_doc!("/metadata_link/props/href.md"), - false, + OptionalKind::No, PropertyType::String, ), ], @@ -699,7 +719,12 @@ pub fn create_component_model() -> Vec { "MetadataTagItem", [ // property("color", true, PropertyType::String), - event("onClick", mark_doc!("/metadata_tag_item/props/onClick.md"), true, []), + event( + "onClick", + mark_doc!("/metadata_tag_item/props/onClick.md"), + OptionalKind::Yes, + [], + ), ], children_string(mark_doc!("/metadata_tag_item/props/children.md")), ); @@ -711,7 +736,7 @@ pub fn create_component_model() -> Vec { [property( "label", mark_doc!("/metadata_tag_list/props/label.md"), - false, + OptionalKind::No, PropertyType::String, )], children_members([member("Item", &metadata_tag_item_component, Arity::ZeroOrMore)], []), @@ -733,7 +758,7 @@ pub fn create_component_model() -> Vec { property( "icon", mark_doc!("/metadata_icon/props/icon.md"), - false, + OptionalKind::No, PropertyType::SharedTypeRef { name: "Icons".to_owned(), }, @@ -741,7 +766,7 @@ pub fn create_component_model() -> Vec { property( "label", mark_doc!("/metadata_icon/props/label.md"), - false, + OptionalKind::No, PropertyType::String, ), ], @@ -755,7 +780,7 @@ pub fn create_component_model() -> Vec { [property( "label", mark_doc!("/metadata_value/props/label.md"), - false, + OptionalKind::No, PropertyType::String, )], children_string(mark_doc!("/metadata_value/props/children.md")), @@ -794,7 +819,7 @@ pub fn create_component_model() -> Vec { [property( "source", mark_doc!("/image/props/source.md"), - false, + OptionalKind::No, PropertyType::SharedTypeRef { name: "ImageLike".to_owned(), }, @@ -809,7 +834,7 @@ pub fn create_component_model() -> Vec { [property( "source", mark_doc!("/svg/props/source.md"), - false, + OptionalKind::No, PropertyType::SharedTypeRef { name: "DataSource".to_owned(), }, @@ -934,13 +959,13 @@ pub fn create_component_model() -> Vec { property( "isLoading", mark_doc!("/list/props/isLoading.md"), - true, + OptionalKind::Yes, PropertyType::Boolean, ), property( "actions", mark_doc!("/detail/props/actions.md"), - true, + OptionalKind::Yes, component_ref(&action_panel_component, Arity::ZeroOrOne), ), ], @@ -961,20 +986,25 @@ pub fn create_component_model() -> Vec { property( "label", mark_doc!("/text_field/props/label.md"), - true, + OptionalKind::Yes, PropertyType::String, ), property( "value", mark_doc!("/text_field/props/value.md"), - true, + OptionalKind::Yes, PropertyType::String, ), event( "onChange", mark_doc!("/text_field/props/onChange.md"), - true, - [property("value", "".to_string(), true, PropertyType::String)], + OptionalKind::Yes, + [property( + "value", + "".to_string(), + OptionalKind::No, + PropertyType::String, + )], ), ], children_none(), @@ -988,20 +1018,25 @@ pub fn create_component_model() -> Vec { property( "label", mark_doc!("/password_field/props/label.md"), - true, + OptionalKind::Yes, PropertyType::String, ), property( "value", mark_doc!("/password_field/props/value.md"), - true, + OptionalKind::Yes, PropertyType::String, ), event( "onChange", mark_doc!("/password_field/props/onChange.md"), - true, - [property("value", "".to_string(), true, PropertyType::String)], + OptionalKind::Yes, + [property( + "value", + "".to_string(), + OptionalKind::No, + PropertyType::String, + )], ), ], children_none(), @@ -1022,26 +1057,31 @@ pub fn create_component_model() -> Vec { property( "label", mark_doc!("/checkbox/props/label.md"), - true, + OptionalKind::Yes, PropertyType::String, ), property( "title", mark_doc!("/checkbox/props/title.md"), - true, + OptionalKind::Yes, PropertyType::String, ), property( "value", mark_doc!("/checkbox/props/value.md"), - true, + OptionalKind::Yes, PropertyType::Boolean, ), event( "onChange", mark_doc!("/checkbox/props/onChange.md"), - true, - [property("value", "".to_string(), false, PropertyType::Boolean)], + OptionalKind::Yes, + [property( + "value", + "".to_string(), + OptionalKind::No, + PropertyType::Boolean, + )], ), ], children_none(), @@ -1081,7 +1121,7 @@ pub fn create_component_model() -> Vec { [property( "value", mark_doc!("/select_item/props/value.md"), - false, + OptionalKind::No, PropertyType::String, )], children_string(mark_doc!("/select_item/props/children.md")), @@ -1092,13 +1132,28 @@ pub fn create_component_model() -> Vec { mark_doc!("/select/description.md"), "Select", [ - property("label", mark_doc!("/select/props/label.md"), true, PropertyType::String), - property("value", mark_doc!("/select/props/value.md"), true, PropertyType::String), + property( + "label", + mark_doc!("/select/props/label.md"), + OptionalKind::Yes, + PropertyType::String, + ), + property( + "value", + mark_doc!("/select/props/value.md"), + OptionalKind::Yes, + PropertyType::String, + ), event( "onChange", mark_doc!("/select/props/onChange.md"), - true, - [property("value", "".to_string(), true, PropertyType::String)], + OptionalKind::Yes, + [property( + "value", + "".to_string(), + OptionalKind::No, + PropertyType::String, + )], ), ], children_members([member("Item", &select_item_component, Arity::ZeroOrMore)], []), @@ -1127,13 +1182,13 @@ pub fn create_component_model() -> Vec { property( "isLoading", mark_doc!("/list/props/isLoading.md"), - true, + OptionalKind::Yes, PropertyType::Boolean, ), property( "actions", mark_doc!("/form/props/actions.md"), - true, + OptionalKind::Yes, component_ref(&action_panel_component, Arity::ZeroOrOne), ), ], @@ -1159,7 +1214,7 @@ pub fn create_component_model() -> Vec { [property( "icon", mark_doc!("/inline_separator/props/icon.md"), - true, + OptionalKind::Yes, PropertyType::SharedTypeRef { name: "Icons".to_owned(), }, @@ -1174,7 +1229,7 @@ pub fn create_component_model() -> Vec { [property( "actions", mark_doc!("/inline/props/actions.md"), - true, + OptionalKind::Yes, component_ref(&action_panel_component, Arity::ZeroOrOne), )], children_members( @@ -1196,19 +1251,19 @@ pub fn create_component_model() -> Vec { property( "title", mark_doc!("/empty_view/props/title.md"), - false, + OptionalKind::No, PropertyType::String, ), property( "description", mark_doc!("/empty_view/props/description.md"), - true, + OptionalKind::Yes, PropertyType::String, ), property( "image", mark_doc!("/empty_view/props/image.md"), - true, + OptionalKind::Yes, PropertyType::SharedTypeRef { name: "ImageLike".to_owned(), }, @@ -1225,13 +1280,13 @@ pub fn create_component_model() -> Vec { property( "text", mark_doc!("/accessory_text/props/text.md"), - false, + OptionalKind::No, PropertyType::String, ), property( "icon", mark_doc!("/accessory_text/props/icon.md"), - true, + OptionalKind::Yes, PropertyType::SharedTypeRef { name: "ImageLike".to_owned(), }, @@ -1239,7 +1294,7 @@ pub fn create_component_model() -> Vec { property( "tooltip", mark_doc!("/accessory_text/props/tooltip.md"), - true, + OptionalKind::Yes, PropertyType::String, ), ], @@ -1254,7 +1309,7 @@ pub fn create_component_model() -> Vec { property( "icon", mark_doc!("/accessory_icon/props/icon.md"), - false, + OptionalKind::No, PropertyType::SharedTypeRef { name: "ImageLike".to_owned(), }, @@ -1262,7 +1317,7 @@ pub fn create_component_model() -> Vec { property( "tooltip", mark_doc!("/accessory_icon/props/tooltip.md"), - true, + OptionalKind::Yes, PropertyType::String, ), ], @@ -1277,20 +1332,25 @@ pub fn create_component_model() -> Vec { property( "value", mark_doc!("/search_bar/props/value.md"), - true, + OptionalKind::Yes, PropertyType::String, ), property( "placeholder", mark_doc!("/search_bar/props/placeholder.md"), - true, + OptionalKind::Yes, PropertyType::String, ), event( "onChange", mark_doc!("/search_bar/props/onChange.md"), - true, - [property("value", "".to_string(), true, PropertyType::String)], + OptionalKind::Yes, + [property( + "value", + "".to_string(), + OptionalKind::No, + PropertyType::String, + )], ), ], children_none(), @@ -1301,23 +1361,28 @@ pub fn create_component_model() -> Vec { mark_doc!("/list_item/description.md"), "ListItem", [ - property("id", mark_doc!("/list_item/props/id.md"), false, PropertyType::String), + property( + "id", + mark_doc!("/list_item/props/id.md"), + OptionalKind::No, + PropertyType::String, + ), property( "title", mark_doc!("/list_item/props/title.md"), - false, + OptionalKind::No, PropertyType::String, ), property( "subtitle", mark_doc!("/list_item/props/subtitle.md"), - true, + OptionalKind::Yes, PropertyType::String, ), property( "icon", mark_doc!("/list_item/props/icon.md"), - true, + OptionalKind::Yes, PropertyType::SharedTypeRef { name: "ImageLike".to_owned(), }, @@ -1325,7 +1390,7 @@ pub fn create_component_model() -> Vec { property( "accessories", mark_doc!("/list_item/props/accessories.md"), - true, + OptionalKind::Yes, PropertyType::Array { item: Box::new(PropertyType::Union { items: vec![ @@ -1347,13 +1412,13 @@ pub fn create_component_model() -> Vec { property( "title", mark_doc!("/list_section/props/title.md"), - false, + OptionalKind::No, PropertyType::String, ), property( "subtitle", mark_doc!("/list_section/props/subtitle.md"), - true, + OptionalKind::Yes, PropertyType::String, ), ], @@ -1368,20 +1433,25 @@ pub fn create_component_model() -> Vec { property( "actions", mark_doc!("/list/props/actions.md"), - true, + OptionalKind::Yes, component_ref(&action_panel_component, Arity::ZeroOrOne), ), property( "isLoading", mark_doc!("/list/props/isLoading.md"), - true, + OptionalKind::Yes, PropertyType::Boolean, ), event( "onItemFocusChange", mark_doc!("/list/props/onItemFocusChange.md"), - true, - [property("itemId", "".to_string(), true, PropertyType::String)], + OptionalKind::Yes, + [property( + "itemId", + "".to_string(), + OptionalKind::Yes, + PropertyType::String, + )], ), ], children_members( @@ -1402,23 +1472,28 @@ pub fn create_component_model() -> Vec { mark_doc!("/grid_item/description.md"), "GridItem", [ - property("id", mark_doc!("/list_item/props/id.md"), false, PropertyType::String), + property( + "id", + mark_doc!("/list_item/props/id.md"), + OptionalKind::No, + PropertyType::String, + ), property( "title", mark_doc!("/grid_item/props/title.md"), - true, + OptionalKind::Yes, PropertyType::String, ), property( "subtitle", mark_doc!("/grid_item/props/subtitle.md"), - true, + OptionalKind::Yes, PropertyType::String, ), property( "accessory", mark_doc!("/grid_item/props/accessory.md"), - true, + OptionalKind::Yes, component_ref(&accessory_icon_component, Arity::ZeroOrOne), ), ], @@ -1433,20 +1508,20 @@ pub fn create_component_model() -> Vec { property( "title", mark_doc!("/grid_section/props/title.md"), - false, + OptionalKind::No, PropertyType::String, ), property( "subtitle", mark_doc!("/grid_section/props/subtitle.md"), - true, + OptionalKind::Yes, PropertyType::String, ), // property("aspectRatio", true, PropertyType::String), property( "columns", mark_doc!("/grid_section/props/columns.md"), - true, + OptionalKind::Yes, PropertyType::Number, ), // fit // inset @@ -1462,20 +1537,20 @@ pub fn create_component_model() -> Vec { property( "isLoading", mark_doc!("/list/props/isLoading.md"), - true, + OptionalKind::Yes, PropertyType::Boolean, ), property( "actions", mark_doc!("/grid/props/actions.md"), - true, + OptionalKind::Yes, component_ref(&action_panel_component, Arity::ZeroOrOne), ), // property("aspectRatio", true, PropertyType::String), property( "columns", mark_doc!("/grid/props/columns.md"), - true, + OptionalKind::Yes, PropertyType::Number, ), // TODO default // fit @@ -1483,8 +1558,13 @@ pub fn create_component_model() -> Vec { event( "onItemFocusChange", mark_doc!("/grid/props/onItemFocusChange.md"), - true, - [property("itemId", "".to_string(), true, PropertyType::String)], + OptionalKind::Yes, + [property( + "itemId", + "".to_string(), + OptionalKind::Yes, + PropertyType::String, + )], ), ], children_members( diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 7989161..0fef45a 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -543,6 +543,7 @@ fn from_intermediate_to_js_event(event: IntermediateUiEvent) -> JsEvent { UiPropertyValue::Number(value) => JsUiPropertyValue::Number { value }, UiPropertyValue::Bool(value) => JsUiPropertyValue::Bool { value }, UiPropertyValue::Undefined => JsUiPropertyValue::Undefined, + UiPropertyValue::Null => JsUiPropertyValue::Null, UiPropertyValue::Array(_) | UiPropertyValue::Bytes(_) | UiPropertyValue::Object(_) => { todo!() } diff --git a/rust/utils_macros/src/widget_deserialization_gen.rs b/rust/utils_macros/src/widget_deserialization_gen.rs index 11f6453..1b19cc6 100644 --- a/rust/utils_macros/src/widget_deserialization_gen.rs +++ b/rust/utils_macros/src/widget_deserialization_gen.rs @@ -4,6 +4,7 @@ use gauntlet_component_model::Arity; use gauntlet_component_model::Children; use gauntlet_component_model::Component; use gauntlet_component_model::ComponentRef; +use gauntlet_component_model::OptionalKind; use gauntlet_component_model::PropertyKind; use gauntlet_component_model::PropertyType; use gauntlet_component_model::SharedType; @@ -135,23 +136,47 @@ pub fn widget_deserialization_gen() -> TokenStream { } }; - if prop.optional { - quote! { - keys.retain(|item| *item != #prop_name); - let #prop_var_name = match extract_object_value(scope, widget_properties, #prop_name) { - Some(property_value) => Some(#deserialize_prop), - None => None, - }; + match prop.optional { + OptionalKind::No => { + quote! { + keys.retain(|item| *item != #prop_name); + let #prop_var_name = match extract_object_value(scope, widget_properties, #prop_name) { + Some(property_value) => #deserialize_prop, + None => { + return Err(Error::required_prop(#prop_name)) + }, + }; + } } - } else { - quote! { - keys.retain(|item| *item != #prop_name); - let #prop_var_name = match extract_object_value(scope, widget_properties, #prop_name) { - Some(property_value) => #deserialize_prop, - None => { - return Err(Error::required_prop(#prop_name)) - }, - }; + OptionalKind::Yes => { + quote! { + keys.retain(|item| *item != #prop_name); + let #prop_var_name = match extract_object_value(scope, widget_properties, #prop_name) { + Some(property_value) => Some(#deserialize_prop), + None => None, + }; + } + } + OptionalKind::YesButComplicated => { + quote! { + keys.retain(|item| *item != #prop_name); + + let key = v8::String::new(scope, #prop_name).unwrap(); + let value = widget_properties.get(scope, key.into()); + + let #prop_var_name = match value { + Some(property_value) => { + if property_value.is_undefined() { + JsOption::Undefined + } else if property_value.is_null() { + JsOption::Null + } else { + JsOption::Value(#deserialize_prop) + } + } + None => JsOption::Undefined + }; + } } } } diff --git a/rust/utils_macros/src/widget_model_gen.rs b/rust/utils_macros/src/widget_model_gen.rs index 35d9a37..99e1877 100644 --- a/rust/utils_macros/src/widget_model_gen.rs +++ b/rust/utils_macros/src/widget_model_gen.rs @@ -6,6 +6,7 @@ use gauntlet_component_model::Arity; use gauntlet_component_model::Children; use gauntlet_component_model::Component; use gauntlet_component_model::ComponentName; +use gauntlet_component_model::OptionalKind; use gauntlet_component_model::Property; use gauntlet_component_model::PropertyKind; use gauntlet_component_model::PropertyType; @@ -422,21 +423,32 @@ pub fn widget_model_gen() -> TokenStream { fn generate_type(property: &Property, name: &ComponentName) -> proc_macro2::TokenStream { match property.optional { - true => { - generate_optional_type( - &property.property_type, - format!("{}{}", name, &property.name.to_case(Case::Pascal)), - ) - } - false => { + OptionalKind::No => { generate_required_type( &property.property_type, Some(format!("{}{}", name, &property.name.to_case(Case::Pascal))), ) } + OptionalKind::Yes => { + generate_optional_type( + &property.property_type, + format!("{}{}", name, &property.name.to_case(Case::Pascal)), + ) + } + OptionalKind::YesButComplicated => { + generate_complicated_optional_type( + &property.property_type, + format!("{}{}", name, &property.name.to_case(Case::Pascal)), + ) + } } } +fn generate_complicated_optional_type(property_type: &PropertyType, union_name: String) -> proc_macro2::TokenStream { + let inner_type = generate_required_type(property_type, Some(union_name)); + quote!(JsOption<#inner_type>) +} + fn generate_optional_type(property_type: &PropertyType, union_name: String) -> proc_macro2::TokenStream { let inner_type = generate_required_type(property_type, Some(union_name)); quote!(Option<#inner_type>) From b4340f4c10fec7c21ffb7e0caf3ad5d532bd7006 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 25 Jul 2025 22:06:17 +0200 Subject: [PATCH 65/91] Fix bundled plugins and examples after breaking change --- bundled_plugins/gauntlet/src/window/shared.tsx | 2 +- dev_plugin/src/hooks-view.tsx | 4 ++-- dev_plugin/src/list-view.tsx | 4 ++-- .../ui_grid/src/search-bar-set-search-text.tsx | 16 +++++++++++++--- .../ui_list/src/search-bar-set-search-text.tsx | 16 +++++++++++++--- 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/bundled_plugins/gauntlet/src/window/shared.tsx b/bundled_plugins/gauntlet/src/window/shared.tsx index 55bbfdf..235b2ef 100644 --- a/bundled_plugins/gauntlet/src/window/shared.tsx +++ b/bundled_plugins/gauntlet/src/window/shared.tsx @@ -22,7 +22,7 @@ export function ListOfWindows({ windows, focusWindow }: { { + onAction={id => { if (id) { focusAndSort(id, focusWindow) return { close: true } diff --git a/dev_plugin/src/hooks-view.tsx b/dev_plugin/src/hooks-view.tsx index 3112ac1..665d148 100644 --- a/dev_plugin/src/hooks-view.tsx +++ b/dev_plugin/src/hooks-view.tsx @@ -30,7 +30,7 @@ export default function ListView(): ReactElement { ) } -function pushPrimaryAction(id: string | undefined, pushView: (component: ReactNode) => void) { +function pushPrimaryAction(id: string | null, pushView: (component: ReactNode) => void) { switch (id) { case "UsePromiseTestBasic": { pushView() @@ -418,4 +418,4 @@ function printState(data: any, error: unknown, isLoading: boolean) { console.dir(data) console.dir(error) console.dir(isLoading) -} \ No newline at end of file +} diff --git a/dev_plugin/src/list-view.tsx b/dev_plugin/src/list-view.tsx index 3a6e717..491c921 100644 --- a/dev_plugin/src/list-view.tsx +++ b/dev_plugin/src/list-view.tsx @@ -5,7 +5,7 @@ import { Environment } from "@project-gauntlet/api/helpers"; export default function ListView(): ReactElement { const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; - const onClick = (id: string | undefined) => { + const onClick = (id: string | null) => { if (id == "print-env") { console.log(Environment.gauntletVersion); console.log(Environment.isDevelopment); @@ -63,4 +63,4 @@ export default function ListView(): ReactElement { ) -} \ No newline at end of file +} diff --git a/example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx b/example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx index 94853c1..959f6e3 100644 --- a/example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx +++ b/example_plugins/plugins/ui_grid/src/search-bar-set-search-text.tsx @@ -2,18 +2,28 @@ import { ReactElement, useState } from "react"; import { Action, ActionPanel, Grid } from "@project-gauntlet/api/components"; export default function SearchBarSetSearchTextExample(): ReactElement { - const [searchText, setSearchText] = useState(""); + const [searchText, setSearchText] = useState(""); return ( - setSearchText(id)}/> + { + // This will be the value in search bar when + // enter is pressed while item is not focused + const unfocused = "item-not-unfocused"; + + setSearchText(id || unfocused) + }} + /> } > - + {/* This id will be the value in search bar when clicked or press enter when item is focused */} + Click me! diff --git a/example_plugins/plugins/ui_list/src/search-bar-set-search-text.tsx b/example_plugins/plugins/ui_list/src/search-bar-set-search-text.tsx index 6670bc7..30812b6 100644 --- a/example_plugins/plugins/ui_list/src/search-bar-set-search-text.tsx +++ b/example_plugins/plugins/ui_list/src/search-bar-set-search-text.tsx @@ -2,18 +2,28 @@ import { ReactElement, useState } from "react"; import { Action, ActionPanel, List } from "@project-gauntlet/api/components"; export default function SearchBarSetSearchTextExample(): ReactElement { - const [searchText, setSearchText] = useState(""); + const [searchText, setSearchText] = useState(""); return ( - setSearchText(id)}/> + { + // This will be the value in search bar when + // enter is pressed while item is not focused + const unfocused = "item-not-unfocused"; + + setSearchText(id || unfocused) + }} + /> } > - + {/* This id will be the value in search bar when clicked or press enter when item is focused */} + ) } From 136b2c2ab6acb82d2cfba918f889f2904824a975 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 25 Jul 2025 22:11:28 +0200 Subject: [PATCH 66/91] Fix more bundled plugins and examples after breaking change --- dev_plugin/src/test-grid-focus.tsx | 2 +- example_plugins/plugins/ui_grid/src/focus.tsx | 2 +- example_plugins/plugins/ui_list/src/focus.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev_plugin/src/test-grid-focus.tsx b/dev_plugin/src/test-grid-focus.tsx index e35cc8e..e61e07b 100644 --- a/dev_plugin/src/test-grid-focus.tsx +++ b/dev_plugin/src/test-grid-focus.tsx @@ -11,7 +11,7 @@ export default function Main(): ReactElement { ); return ( - console.log("onItemFocusChange", id)}> + console.log("onItemFocusChange", id)}> {content} {content} {content} diff --git a/example_plugins/plugins/ui_grid/src/focus.tsx b/example_plugins/plugins/ui_grid/src/focus.tsx index 4f8724d..c7bca16 100644 --- a/example_plugins/plugins/ui_grid/src/focus.tsx +++ b/example_plugins/plugins/ui_grid/src/focus.tsx @@ -2,7 +2,7 @@ import { ReactElement, useState } from "react"; import { Grid } from "@project-gauntlet/api/components"; export default function FocusExample(): ReactElement { - const [id, setId] = useState(undefined); + const [id, setId] = useState(null); const content = (text: string) => ( diff --git a/example_plugins/plugins/ui_list/src/focus.tsx b/example_plugins/plugins/ui_list/src/focus.tsx index 8faba22..b112137 100644 --- a/example_plugins/plugins/ui_list/src/focus.tsx +++ b/example_plugins/plugins/ui_list/src/focus.tsx @@ -2,7 +2,7 @@ import { ReactElement, useState } from "react"; import { List } from "@project-gauntlet/api/components"; export default function FocusExample(): ReactElement { - const [id, setId] = useState(undefined); + const [id, setId] = useState(null); return ( From f41ab9233bbaeaaa9c7d7670743b98c5cfd7a9c5 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 27 Jul 2025 16:50:07 +0200 Subject: [PATCH 67/91] Merge settings app into the main app --- Cargo.lock | 16 - Cargo.toml | 2 - rust/cli/Cargo.toml | 1 - rust/cli/src/lib.rs | 4 +- rust/client/src/ui/mod.rs | 148 +++--- rust/client/src/ui/server.rs | 235 +-------- .../src/ui/settings}/components/mod.rs | 0 .../settings}/components/shortcut_selector.rs | 8 +- .../src/ui/settings}/components/spinner.rs | 0 rust/client/src/ui/settings/mod.rs | 4 + .../src => client/src/ui/settings}/theme.rs | 19 +- .../src/ui/settings}/theme/button.rs | 20 +- .../src/ui/settings}/theme/checkbox.rs | 12 +- .../src/ui/settings}/theme/container.rs | 21 +- .../src/ui/settings}/theme/pick_list.rs | 16 +- .../src/ui/settings}/theme/rule.rs | 4 +- .../src/ui/settings}/theme/scrollable.rs | 4 +- .../ui/settings}/theme/shortcut_selector.rs | 16 +- .../src/ui/settings}/theme/text.rs | 8 +- .../src/ui/settings}/theme/text_input.rs | 16 +- .../src => client/src/ui/settings}/ui.rs | 401 ++++++++-------- .../src/ui/settings}/views/general.rs | 132 +++--- .../src/ui/settings}/views/mod.rs | 0 .../src/ui/settings}/views/plugins.rs | 448 +++++++++--------- .../ui/settings}/views/plugins/preferences.rs | 10 +- .../src/ui/settings}/views/plugins/table.rs | 39 +- rust/client/src/ui/windows/mod.rs | 8 +- rust/common/src/lib.rs | 20 - rust/common/src/rpc/backend_api.rs | 84 ---- rust/common/src/rpc/backend_server.rs | 22 +- rust/common/src/rpc/frontend_api.rs | 2 + rust/common/src/rpc/server_grpc_api.rs | 67 --- rust/common_plugin_runtime/src/api.rs | 1 + rust/management_client/Cargo.toml | 17 - rust/management_client/src/lib.rs | 8 - rust/management_client/src/main.rs | 5 - rust/plugin_runtime/src/plugins/settings.rs | 25 +- rust/server/src/plugins/data_db_repository.rs | 1 - rust/server/src/plugins/js.rs | 6 + rust/server/src/plugins/loader.rs | 4 +- rust/server/src/plugins/mod.rs | 38 +- rust/server/src/rpc.rs | 204 -------- schema/backend.proto | 3 - 43 files changed, 679 insertions(+), 1420 deletions(-) rename rust/{management_client/src => client/src/ui/settings}/components/mod.rs (100%) rename rust/{management_client/src => client/src/ui/settings}/components/shortcut_selector.rs (98%) rename rust/{management_client/src => client/src/ui/settings}/components/spinner.rs (100%) create mode 100644 rust/client/src/ui/settings/mod.rs rename rust/{management_client/src => client/src/ui/settings}/theme.rs (83%) rename rust/{management_client/src => client/src/ui/settings}/theme/button.rs (92%) rename rust/{management_client/src => client/src/ui/settings}/theme/checkbox.rs (85%) rename rust/{management_client/src => client/src/ui/settings}/theme/container.rs (72%) rename rust/{management_client/src => client/src/ui/settings}/theme/pick_list.rs (82%) rename rust/{management_client/src => client/src/ui/settings}/theme/rule.rs (81%) rename rust/{management_client/src => client/src/ui/settings}/theme/scrollable.rs (96%) rename rust/{management_client/src => client/src/ui/settings}/theme/shortcut_selector.rs (79%) rename rust/{management_client/src => client/src/ui/settings}/theme/text.rs (82%) rename rust/{management_client/src => client/src/ui/settings}/theme/text_input.rs (86%) rename rust/{management_client/src => client/src/ui/settings}/ui.rs (67%) rename rust/{management_client/src => client/src/ui/settings}/views/general.rs (66%) rename rust/{management_client/src => client/src/ui/settings}/views/mod.rs (100%) rename rust/{management_client/src => client/src/ui/settings}/views/plugins.rs (66%) rename rust/{management_client/src => client/src/ui/settings}/views/plugins/preferences.rs (98%) rename rust/{management_client/src => client/src/ui/settings}/views/plugins/table.rs (95%) delete mode 100644 rust/management_client/Cargo.toml delete mode 100644 rust/management_client/src/lib.rs delete mode 100644 rust/management_client/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 4a0eb30..a63c9c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4500,7 +4500,6 @@ dependencies = [ "clap", "gauntlet-client", "gauntlet-common", - "gauntlet-management-client", "gauntlet-plugin-runtime", "gauntlet-server", "gauntlet-utils", @@ -4598,21 +4597,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "gauntlet-management-client" -version = "0.0.0" -dependencies = [ - "anyhow", - "gauntlet-common", - "gauntlet-common-ui", - "gauntlet-utils", - "iced", - "iced_fonts", - "itertools 0.13.0", - "tracing", - "tracing-subscriber", -] - [[package]] name = "gauntlet-manifest-schema" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 724ba55..eb719e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ repository = "https://github.com/project-gauntlet/gauntlet" [workspace] members = [ - "rust/management_client", "rust/client", "rust/server", "rust/common", @@ -35,7 +34,6 @@ gauntlet-common = { path = "./rust/common" } gauntlet-common-ui = { path = "./rust/common_ui" } gauntlet-common-plugin-runtime = { path = "./rust/common_plugin_runtime" } gauntlet-plugin-runtime = { path = "./rust/plugin_runtime" } -gauntlet-management-client = { path = "./rust/management_client" } gauntlet-client = { path = "./rust/client" } gauntlet-server = { path = "./rust/server" } gauntlet-utils = { path = "./rust/utils" } diff --git a/rust/cli/Cargo.toml b/rust/cli/Cargo.toml index b5a08e2..1043dda 100644 --- a/rust/cli/Cargo.toml +++ b/rust/cli/Cargo.toml @@ -4,7 +4,6 @@ edition.workspace = true [dependencies] # workspaces -gauntlet-management-client.workspace = true gauntlet-client.workspace = true gauntlet-server.workspace = true gauntlet-plugin-runtime.workspace = true diff --git a/rust/cli/src/lib.rs b/rust/cli/src/lib.rs index 259e827..2d79552 100644 --- a/rust/cli/src/lib.rs +++ b/rust/cli/src/lib.rs @@ -7,10 +7,10 @@ use std::time::UNIX_EPOCH; use clap::Parser; use gauntlet_common::cli::is_server_running; +use gauntlet_common::cli::open_settings_window; use gauntlet_common::cli::open_window; use gauntlet_common::cli::run_action; use gauntlet_common::dirs::Dirs; -use gauntlet_management_client::start_management_client; use gauntlet_server::PLUGIN_CONNECT_ENV; use gauntlet_server::PLUGIN_UUID_ENV; use tracing_subscriber::EnvFilter; @@ -110,7 +110,7 @@ pub fn init() { Some(command) => { match command { Commands::Open => open_window(), - Commands::Settings => start_management_client(), + Commands::Settings => open_settings_window(), Commands::Run { plugin_id, entrypoint_id, diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index d015c67..2c8e063 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -52,6 +52,7 @@ use iced::widget::text; use iced::widget::text::Shaping; use iced::widget::text_input; use iced::widget::text_input::focus; +use iced::widget::themer; use iced::window; use iced_fonts::BOOTSTRAP_FONT_BYTES; @@ -68,6 +69,7 @@ mod custom_widgets; mod grid_navigation; mod scroll_handle; mod search_list; +mod settings; mod state; #[cfg(any(target_os = "macos", target_os = "windows"))] mod sys_tray; @@ -89,6 +91,13 @@ use crate::ui::scenario_runner::ScenarioRunnerMsg; use crate::ui::scenario_runner::handle_scenario_runner_msg; use crate::ui::scroll_handle::ScrollHandle; use crate::ui::server::handle_server_message; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::ui::SettingsMsg; +use crate::ui::settings::ui::SettingsParams; +use crate::ui::settings::ui::SettingsWindowState; +use crate::ui::settings::ui::subscription_settings; +use crate::ui::settings::ui::update_settings; +use crate::ui::settings::ui::view_settings; use crate::ui::state::ErrorViewData; use crate::ui::state::Focus; use crate::ui::state::GlobalState; @@ -100,8 +109,8 @@ use crate::ui::widget::action_panel::ActionPanel; use crate::ui::widget::action_panel::ActionPanelItem; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::root::render_root; +use crate::ui::windows::MainWindowState; use crate::ui::windows::WindowActionMsg; -use crate::ui::windows::WindowState; #[cfg(target_os = "linux")] use crate::ui::windows::x11_focus::x11_linux_focus_change_subscription; @@ -112,7 +121,8 @@ pub struct AppModel { #[cfg(any(target_os = "macos", target_os = "windows"))] _tray_icon: tray_icon::TrayIcon, theme: GauntletComplexTheme, - window: WindowState, + main_window_state: MainWindowState, + settings_window_state: SettingsWindowState, // ephemeral state prompt: String, @@ -197,10 +207,7 @@ pub enum AppMsg { plugin_preferences_required: bool, entrypoint_preferences_required: bool, }, - OpenSettingsPreferences { - plugin_id: PluginId, - entrypoint_id: Option, - }, + OpenSettings(SettingsParams), OnOpenView { action_shortcuts: HashMap, }, @@ -278,6 +285,7 @@ pub enum AppMsg { display: String, }, HandleScenario(ScenarioRunnerMsg), + Settings(SettingsMsg), } pub fn run(minimized: bool, scenario_runner_data: Option) { @@ -333,7 +341,7 @@ fn new(minimized: bool, #[allow(unused)] scenario_runner_data: Option String { - if Some(window) == state.window.main_window_id { - "Gauntlet".to_owned() - } else { - "Gauntlet HUD".to_owned() + match window { + _ if Some(window) == state.main_window_state.main_window_id => "Gauntlet".to_owned(), + _ if Some(window) == state.settings_window_state.settings_window_id => "Gauntlet Settings".to_owned(), + _ => "Gauntlet HUD".to_owned(), } } @@ -648,7 +664,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } AppMsg::IcedEvent(window_id, Event::Keyboard(event)) => { - let Some(main_window_id) = state.window.main_window_id else { + let Some(main_window_id) = state.main_window_state.main_window_id else { return Task::none(); }; @@ -742,13 +758,20 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } AppMsg::IcedEvent(window_id, Event::Window(window::Event::Focused)) => { - state.window.handle_focused_event(window_id) + state.main_window_state.handle_focused_event(window_id) } AppMsg::IcedEvent(window_id, Event::Window(window::Event::Unfocused)) => { - state.window.handle_unfocused_event(window_id) + state.main_window_state.handle_unfocused_event(window_id) } AppMsg::IcedEvent(window_id, Event::Window(window::Event::Moved(point))) => { - state.window.handle_move_event(window_id, point) + state.main_window_state.handle_move_event(window_id, point) + } + AppMsg::IcedEvent(window_id, Event::Window(window::Event::Closed)) => { + if state.settings_window_state.settings_window_id == Some(window_id) { + Task::done(AppMsg::Settings(SettingsMsg::WindowDestroyed)) + } else { + Task::none() + } } AppMsg::IcedEvent(_, _) => Task::none(), AppMsg::WidgetEvent { @@ -807,10 +830,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { }, ) } - AppMsg::OpenSettingsPreferences { - plugin_id, - entrypoint_id, - } => state.open_settings_window_preferences(plugin_id, entrypoint_id), + AppMsg::OpenSettings(params) => Task::done(AppMsg::Settings(SettingsMsg::OpenSettings(params))), AppMsg::OnOpenView { action_shortcuts } => { match &mut state.global_state { GlobalState::MainView { @@ -1086,7 +1106,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } AppMsg::FocusPluginViewSearchBar { widget_id } => state.client_context.focus_search_bar(widget_id), - AppMsg::WindowAction(action) => state.window.handle_action(action), + AppMsg::WindowAction(action) => state.main_window_state.handle_action(action), AppMsg::SetTheme { theme } => { state.theme = GauntletComplexTheme::new(theme); @@ -1192,17 +1212,28 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } AppMsg::HandleScenario(msg) => { - handle_scenario_runner_msg(msg, state.application_manager.clone(), state.window.main_window_id) - .map(AppMsg::HandleScenario) + handle_scenario_runner_msg( + msg, + state.application_manager.clone(), + state.main_window_state.main_window_id, + ) + .map(AppMsg::HandleScenario) + } + AppMsg::Settings(msg) => { + update_settings(&mut state.settings_window_state, &state.global_hotkey_manager, msg).map(AppMsg::Settings) } } } fn view(state: &AppModel, window: window::Id) -> Element<'_, AppMsg> { - if Some(window) == state.window.main_window_id { - view_main(state) - } else { - view_hud(state) + match window { + _ if Some(window) == state.main_window_state.main_window_id => view_main(state), + _ if Some(window) == state.settings_window_state.settings_window_id => { + let themer: Element<_> = themer(GauntletSettingsTheme, view_settings(&state.settings_window_state)).into(); + + themer.map(AppMsg::Settings) + } + _ => view_hud(state), } } @@ -1257,27 +1288,25 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { "Before using, plugin and entrypoint preferences need to be specified"; // note: // we open plugin view and not entrypoint even though both need to be specified - let msg = AppMsg::OpenSettingsPreferences { + let msg = AppMsg::OpenSettings(SettingsParams::PluginPreferences { plugin_id: plugin_id.clone(), - entrypoint_id: None, - }; + }); (description_text, msg) } (false, true) => { // TODO do not show "entrypoint" name to user let description_text = "Before using, entrypoint preferences need to be specified"; - let msg = AppMsg::OpenSettingsPreferences { + let msg = AppMsg::OpenSettings(SettingsParams::EntrypointPreferences { plugin_id: plugin_id.clone(), - entrypoint_id: Some(entrypoint_id.clone()), - }; + entrypoint_id: entrypoint_id.clone(), + }); (description_text, msg) } (true, false) => { let description_text = "Before using, plugin preferences need to be specified"; - let msg = AppMsg::OpenSettingsPreferences { + let msg = AppMsg::OpenSettings(SettingsParams::PluginPreferences { plugin_id: plugin_id.clone(), - entrypoint_id: None, - }; + }); (description_text, msg) } (false, false) => unreachable!(), @@ -1744,7 +1773,9 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { } fn subscription(#[allow(unused)] state: &AppModel) -> Subscription { - let events_subscription = event::listen_with(|event, status, window_id| { + let mut subscriptions = vec![]; + + subscriptions.push(event::listen_with(|event, status, window_id| { match status { event::Status::Ignored => Some(AppMsg::IcedEvent(window_id, event)), event::Status::Captured => { @@ -1757,25 +1788,23 @@ fn subscription(#[allow(unused)] state: &AppModel) -> Subscription { } } } - }); + })); - #[allow(unused_mut)] - let mut subscriptions = vec![ - Subscription::run(|| { - stream::channel(10, async move |sender| { - register_global_shortcut_listener(sender.clone()); + subscriptions.push(Subscription::run(|| { + stream::channel(10, async move |sender| { + register_global_shortcut_listener(sender.clone()); - std::future::pending::<()>().await; + std::future::pending::<()>().await; - unreachable!() - }) - .map(AppMsg::HandleGlobalShortcut) - }), - events_subscription, - ]; + unreachable!() + }) + .map(AppMsg::HandleGlobalShortcut) + })); + + subscriptions.push(subscription_settings(&state.settings_window_state).map(AppMsg::Settings)); #[cfg(target_os = "linux")] - if !state.window.wayland { + if !state.main_window_state.wayland { subscriptions.push(x11_linux_focus_change_subscription()) } @@ -1951,17 +1980,6 @@ impl AppModel { Task::done(msg) } - fn open_settings_window_preferences( - &self, - plugin_id: PluginId, - entrypoint_id: Option, - ) -> Task { - self.application_manager - .open_settings_window_preferences(plugin_id, entrypoint_id); - - Task::none() - } - fn inline_view_shortcuts(&self) -> Task { let result = self .application_manager @@ -1998,11 +2016,7 @@ impl AppModel { modifier_control: cfg!(not(target_os = "macos")), modifier_alt: false, modifier_meta: cfg!(target_os = "macos"), - }) => { - self.application_manager.open_settings_window(); - - Task::none() - } + }) => Task::done(AppMsg::OpenSettings(SettingsParams::Default)), Some(PhysicalShortcut { physical_key: PhysicalKey::KeyK, modifier_shift: false, diff --git a/rust/client/src/ui/server.rs b/rust/client/src/ui/server.rs index 91ae342..6c3066c 100644 --- a/rust/client/src/ui/server.rs +++ b/rust/client/src/ui/server.rs @@ -2,7 +2,6 @@ use std::ops::Deref; use std::sync::Arc; use std::sync::Mutex; -use anyhow::anyhow; use gauntlet_common::model::UiSetupData; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; @@ -22,6 +21,7 @@ use tokio::sync::RwLock as TokioRwLock; use crate::ui::AppModel; use crate::ui::AppMsg; +use crate::ui::settings::ui::SettingsParams; #[cfg(target_os = "linux")] use crate::ui::wayland::layer_shell_supported; use crate::ui::windows::WindowActionMsg; @@ -234,6 +234,11 @@ async fn request_loop( action_index, } } + FrontendApiRequestData::ShowSettings {} => { + responder.respond(Ok(FrontendApiResponseData::ShowSettings { data: () })); + + AppMsg::OpenSettings(SettingsParams::Default) + } } }; @@ -260,9 +265,7 @@ pub fn handle_server_message( ServerGrpcApiRequestData::ShowSettingsWindow {} => { responder.respond(Ok(ServerGrpcApiResponseData::ShowSettingsWindow { data: () })); - state.application_manager.open_settings_window(); - - Task::none() + Task::done(AppMsg::OpenSettings(SettingsParams::Default)) } ServerGrpcApiRequestData::RunAction { plugin_id, @@ -293,230 +296,6 @@ pub fn handle_server_message( responder.respond(result); - Task::none() - } - ServerGrpcApiRequestData::Plugins {} => { - let result = state - .application_manager - .plugins() - .map(|data| ServerGrpcApiResponseData::Plugins { data }); - - responder.respond(result.into()); - - Task::none() - } - ServerGrpcApiRequestData::SetPluginState { plugin_id, enabled } => { - let result = state - .application_manager - .set_plugin_state(plugin_id.clone(), *enabled) - .map(|data| ServerGrpcApiResponseData::SetPluginState { data }); - - responder.respond(result.into()); - - Task::none() - } - ServerGrpcApiRequestData::SetEntrypointState { - plugin_id, - entrypoint_id, - enabled, - } => { - let result = state - .application_manager - .set_entrypoint_state(plugin_id.clone(), entrypoint_id.clone(), *enabled) - .map(|data| ServerGrpcApiResponseData::SetEntrypointState { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetGlobalShortcut { shortcut } => { - let Some(global_hotkey_manager) = &state.global_hotkey_manager else { - responder.respond(Err(anyhow!("Global hotkey manager is disabled"))); - return Task::none(); - }; - - let result = state - .application_manager - .set_global_shortcut(global_hotkey_manager, shortcut.clone()); - - responder.respond(Ok(ServerGrpcApiResponseData::SetGlobalShortcut { data: result })); - - Task::none() - } - ServerGrpcApiRequestData::GetGlobalShortcut {} => { - let result = state - .application_manager - .get_global_shortcut() - .map(|data| ServerGrpcApiResponseData::GetGlobalShortcut { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetGlobalEntrypointShortcut { - plugin_id, - entrypoint_id, - shortcut, - } => { - let Some(global_hotkey_manager) = &state.global_hotkey_manager else { - responder.respond(Err(anyhow!("Global hotkey manager is disabled"))); - return Task::none(); - }; - - let result = state - .application_manager - .set_global_entrypoint_shortcut( - global_hotkey_manager, - plugin_id.clone(), - entrypoint_id.clone(), - shortcut.clone(), - ) - .map(|data| ServerGrpcApiResponseData::SetGlobalEntrypointShortcut { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::GetGlobalEntrypointShortcuts {} => { - let result = state - .application_manager - .get_global_entrypoint_shortcut() - .map(|data| ServerGrpcApiResponseData::GetGlobalEntrypointShortcuts { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetEntrypointSearchAlias { - plugin_id, - entrypoint_id, - alias, - } => { - let result = state - .application_manager - .set_entrypoint_search_alias(plugin_id.clone(), entrypoint_id.clone(), alias.clone()) - .map(|data| ServerGrpcApiResponseData::SetEntrypointSearchAlias { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::GetEntrypointSearchAliases {} => { - let result = state - .application_manager - .get_entrypoint_search_aliases() - .map(|data| ServerGrpcApiResponseData::GetEntrypointSearchAliases { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetTheme { theme } => { - let application_manager = state.application_manager.clone(); - let theme = theme.clone(); - - Task::future(async move { - let result = application_manager - .set_theme(theme) - .await - .map(|data| ServerGrpcApiResponseData::SetTheme { data }); - - responder.respond(result); - - AppMsg::Noop - }) - } - ServerGrpcApiRequestData::GetTheme {} => { - let result = state - .application_manager - .get_theme() - .map(|data| ServerGrpcApiResponseData::GetTheme { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetWindowPositionMode { mode } => { - let application_manager = state.application_manager.clone(); - let mode = mode.clone(); - - Task::future(async move { - let result = application_manager - .set_window_position_mode(mode) - .await - .map(|data| ServerGrpcApiResponseData::SetWindowPositionMode { data }); - - responder.respond(result); - - AppMsg::Noop - }) - } - ServerGrpcApiRequestData::GetWindowPositionMode {} => { - let result = state - .application_manager - .get_window_position_mode() - .map(|data| ServerGrpcApiResponseData::GetWindowPositionMode { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetPreferenceValue { - plugin_id, - entrypoint_id, - preference_id, - preference_value, - } => { - let result = state - .application_manager - .set_preference_value( - plugin_id.clone(), - entrypoint_id.clone(), - preference_id.clone(), - preference_value.clone(), - ) - .map(|data| ServerGrpcApiResponseData::SetPreferenceValue { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::DownloadPlugin { plugin_id } => { - let result = state - .application_manager - .download_plugin(plugin_id.clone()) - .map(|data| ServerGrpcApiResponseData::DownloadPlugin { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::DownloadStatus {} => { - let data = state.application_manager.download_status(); - - responder.respond(Ok(ServerGrpcApiResponseData::DownloadStatus { data })); - - Task::none() - } - ServerGrpcApiRequestData::RemovePlugin { plugin_id } => { - let result = state - .application_manager - .remove_plugin(plugin_id.clone()) - .map(|data| ServerGrpcApiResponseData::RemovePlugin { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::WaylandGlobalShortcutsEnabled {} => { - let result = state.application_manager.config().map(|data| { - ServerGrpcApiResponseData::WaylandGlobalShortcutsEnabled { - data: data.wayland_use_legacy_x11_api, - } - }); - - responder.respond(result); - Task::none() } } diff --git a/rust/management_client/src/components/mod.rs b/rust/client/src/ui/settings/components/mod.rs similarity index 100% rename from rust/management_client/src/components/mod.rs rename to rust/client/src/ui/settings/components/mod.rs diff --git a/rust/management_client/src/components/shortcut_selector.rs b/rust/client/src/ui/settings/components/shortcut_selector.rs similarity index 98% rename from rust/management_client/src/components/shortcut_selector.rs rename to rust/client/src/ui/settings/components/shortcut_selector.rs index 277ea19..fe56abc 100644 --- a/rust/management_client/src/components/shortcut_selector.rs +++ b/rust/client/src/ui/settings/components/shortcut_selector.rs @@ -33,10 +33,10 @@ use iced::widget::tooltip; use iced::widget::tooltip::Position; use iced_fonts::bootstrap::exclamation_triangle_fill; -use crate::theme::Element; -use crate::theme::GauntletSettingsTheme; -use crate::theme::container::ContainerStyle; -use crate::theme::text::TextStyle; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::container::ContainerStyle; +use crate::ui::settings::theme::text::TextStyle; pub struct ShortcutData { pub shortcut: Option, diff --git a/rust/management_client/src/components/spinner.rs b/rust/client/src/ui/settings/components/spinner.rs similarity index 100% rename from rust/management_client/src/components/spinner.rs rename to rust/client/src/ui/settings/components/spinner.rs diff --git a/rust/client/src/ui/settings/mod.rs b/rust/client/src/ui/settings/mod.rs new file mode 100644 index 0000000..5fb2004 --- /dev/null +++ b/rust/client/src/ui/settings/mod.rs @@ -0,0 +1,4 @@ +mod components; +pub mod theme; +pub mod ui; +pub mod views; diff --git a/rust/management_client/src/theme.rs b/rust/client/src/ui/settings/theme.rs similarity index 83% rename from rust/management_client/src/theme.rs rename to rust/client/src/ui/settings/theme.rs index e3f1134..f7987fc 100644 --- a/rust/management_client/src/theme.rs +++ b/rust/client/src/ui/settings/theme.rs @@ -1,7 +1,3 @@ -use iced::theme; -use iced::theme::Palette; -use iced::theme::Style; - pub mod button; pub mod checkbox; pub mod container; @@ -14,22 +10,9 @@ pub mod text_input; pub type Element<'a, Message> = iced::Element<'a, Message, GauntletSettingsTheme>; -#[derive(Default)] +#[derive(Default, Clone)] pub struct GauntletSettingsTheme; -impl theme::Base for GauntletSettingsTheme { - fn base(&self) -> Style { - Style { - background_color: BACKGROUND_DARKEST.to_iced(), - text_color: TEXT_LIGHTEST.to_iced(), - } - } - - fn palette(&self) -> Option { - Some(Palette::FERRA) - } -} - // keep colors more or less in sync with main ui #[allow(unused)] pub const NOT_INTENDED_TO_BE_USED: ThemeColor = ThemeColor::new(0xAF5BFF, 1.0); diff --git a/rust/management_client/src/theme/button.rs b/rust/client/src/ui/settings/theme/button.rs similarity index 92% rename from rust/management_client/src/theme/button.rs rename to rust/client/src/ui/settings/theme/button.rs index 5e05950..5595e27 100644 --- a/rust/management_client/src/theme/button.rs +++ b/rust/client/src/ui/settings/theme/button.rs @@ -3,16 +3,16 @@ use iced::widget::button; use iced::widget::button::Status; use iced::widget::button::Style; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_LIGHTER; -use crate::theme::BUTTON_BORDER_RADIUS; -use crate::theme::DANGER; -use crate::theme::GauntletSettingsTheme; -use crate::theme::PRIMARY; -use crate::theme::PRIMARY_HOVERED; -use crate::theme::SUCCESS; -use crate::theme::TEXT_DARKEST; -use crate::theme::TEXT_LIGHTEST; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_LIGHTER; +use crate::ui::settings::theme::BUTTON_BORDER_RADIUS; +use crate::ui::settings::theme::DANGER; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::PRIMARY; +use crate::ui::settings::theme::PRIMARY_HOVERED; +use crate::ui::settings::theme::SUCCESS; +use crate::ui::settings::theme::TEXT_DARKEST; +use crate::ui::settings::theme::TEXT_LIGHTEST; pub enum ButtonStyle { Primary, diff --git a/rust/management_client/src/theme/checkbox.rs b/rust/client/src/ui/settings/theme/checkbox.rs similarity index 85% rename from rust/management_client/src/theme/checkbox.rs rename to rust/client/src/ui/settings/theme/checkbox.rs index 4e744d6..b9b9cf6 100644 --- a/rust/management_client/src/theme/checkbox.rs +++ b/rust/client/src/ui/settings/theme/checkbox.rs @@ -3,12 +3,12 @@ use iced::widget::checkbox; use iced::widget::checkbox::Status; use iced::widget::checkbox::Style; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_DARKEST; -use crate::theme::BACKGROUND_LIGHTER; -use crate::theme::GauntletSettingsTheme; -use crate::theme::PRIMARY; -use crate::theme::PRIMARY_HOVERED; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_DARKEST; +use crate::ui::settings::theme::BACKGROUND_LIGHTER; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::PRIMARY; +use crate::ui::settings::theme::PRIMARY_HOVERED; impl checkbox::Catalog for GauntletSettingsTheme { type Class<'a> = (); diff --git a/rust/management_client/src/theme/container.rs b/rust/client/src/ui/settings/theme/container.rs similarity index 72% rename from rust/management_client/src/theme/container.rs rename to rust/client/src/ui/settings/theme/container.rs index 692b5f3..e416a5a 100644 --- a/rust/management_client/src/theme/container.rs +++ b/rust/client/src/ui/settings/theme/container.rs @@ -1,15 +1,19 @@ +use iced::Background; use iced::Border; use iced::Color; use iced::widget::container; use iced::widget::container::Style; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_LIGHTER; -use crate::theme::DANGER; -use crate::theme::GauntletSettingsTheme; -use crate::theme::TRANSPARENT; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_DARKEST; +use crate::ui::settings::theme::BACKGROUND_LIGHTER; +use crate::ui::settings::theme::DANGER; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::TEXT_LIGHTEST; +use crate::ui::settings::theme::TRANSPARENT; pub enum ContainerStyle { + WindowRoot, Transparent, Box, TextInputMissingValue, @@ -60,6 +64,13 @@ impl container::Catalog for GauntletSettingsTheme { ..Default::default() } } + ContainerStyle::WindowRoot => { + Style { + background: Some(Background::Color(BACKGROUND_DARKEST.to_iced())), + text_color: Some(TEXT_LIGHTEST.to_iced()), + ..Default::default() + } + } } } } diff --git a/rust/management_client/src/theme/pick_list.rs b/rust/client/src/ui/settings/theme/pick_list.rs similarity index 82% rename from rust/management_client/src/theme/pick_list.rs rename to rust/client/src/ui/settings/theme/pick_list.rs index bd6c159..7edcf56 100644 --- a/rust/management_client/src/theme/pick_list.rs +++ b/rust/client/src/ui/settings/theme/pick_list.rs @@ -2,14 +2,14 @@ use iced::Border; use iced::overlay; use iced::widget::pick_list; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_DARKEST; -use crate::theme::BUTTON_BORDER_RADIUS; -use crate::theme::GauntletSettingsTheme; -use crate::theme::PRIMARY; -use crate::theme::PRIMARY_HOVERED; -use crate::theme::TEXT_DARKEST; -use crate::theme::TEXT_LIGHTEST; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_DARKEST; +use crate::ui::settings::theme::BUTTON_BORDER_RADIUS; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::PRIMARY; +use crate::ui::settings::theme::PRIMARY_HOVERED; +use crate::ui::settings::theme::TEXT_DARKEST; +use crate::ui::settings::theme::TEXT_LIGHTEST; impl pick_list::Catalog for GauntletSettingsTheme { type Class<'a> = (); diff --git a/rust/management_client/src/theme/rule.rs b/rust/client/src/ui/settings/theme/rule.rs similarity index 81% rename from rust/management_client/src/theme/rule.rs rename to rust/client/src/ui/settings/theme/rule.rs index ae9ea52..35a9124 100644 --- a/rust/management_client/src/theme/rule.rs +++ b/rust/client/src/ui/settings/theme/rule.rs @@ -1,8 +1,8 @@ use iced::widget::rule; use iced::widget::rule::Style; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::GauntletSettingsTheme; impl rule::Catalog for GauntletSettingsTheme { type Class<'a> = (); diff --git a/rust/management_client/src/theme/scrollable.rs b/rust/client/src/ui/settings/theme/scrollable.rs similarity index 96% rename from rust/management_client/src/theme/scrollable.rs rename to rust/client/src/ui/settings/theme/scrollable.rs index fafed5f..e4d3343 100644 --- a/rust/management_client/src/theme/scrollable.rs +++ b/rust/client/src/ui/settings/theme/scrollable.rs @@ -6,8 +6,8 @@ use iced::widget::scrollable; use iced::widget::scrollable::Status; use iced::widget::scrollable::Style; -use crate::theme::GauntletSettingsTheme; -use crate::theme::PRIMARY; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::PRIMARY; impl scrollable::Catalog for GauntletSettingsTheme { type Class<'a> = (); diff --git a/rust/management_client/src/theme/shortcut_selector.rs b/rust/client/src/ui/settings/theme/shortcut_selector.rs similarity index 79% rename from rust/management_client/src/theme/shortcut_selector.rs rename to rust/client/src/ui/settings/theme/shortcut_selector.rs index 09dc383..c416ae0 100644 --- a/rust/management_client/src/theme/shortcut_selector.rs +++ b/rust/client/src/ui/settings/theme/shortcut_selector.rs @@ -1,14 +1,14 @@ use iced::Border; use iced::widget::container::Style; -use crate::components::shortcut_selector; -use crate::components::shortcut_selector::Status; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_LIGHTER; -use crate::theme::BUTTON_BORDER_RADIUS; -use crate::theme::GauntletSettingsTheme; -use crate::theme::PRIMARY; -use crate::theme::TRANSPARENT; +use crate::ui::settings::components::shortcut_selector; +use crate::ui::settings::components::shortcut_selector::Status; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_LIGHTER; +use crate::ui::settings::theme::BUTTON_BORDER_RADIUS; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::PRIMARY; +use crate::ui::settings::theme::TRANSPARENT; impl shortcut_selector::Catalog for GauntletSettingsTheme { type Class<'a> = (); diff --git a/rust/management_client/src/theme/text.rs b/rust/client/src/ui/settings/theme/text.rs similarity index 82% rename from rust/management_client/src/theme/text.rs rename to rust/client/src/ui/settings/theme/text.rs index be46ec7..a32fd55 100644 --- a/rust/management_client/src/theme/text.rs +++ b/rust/client/src/ui/settings/theme/text.rs @@ -1,10 +1,10 @@ use iced::widget::text; use iced::widget::text::Style; -use crate::theme::DANGER_BRIGHT; -use crate::theme::GauntletSettingsTheme; -use crate::theme::SUCCESS; -use crate::theme::TEXT_DARKER; +use crate::ui::settings::theme::DANGER_BRIGHT; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::SUCCESS; +use crate::ui::settings::theme::TEXT_DARKER; pub enum TextStyle { Default, diff --git a/rust/management_client/src/theme/text_input.rs b/rust/client/src/ui/settings/theme/text_input.rs similarity index 86% rename from rust/management_client/src/theme/text_input.rs rename to rust/client/src/ui/settings/theme/text_input.rs index cf58f28..8e35f7e 100644 --- a/rust/management_client/src/theme/text_input.rs +++ b/rust/client/src/ui/settings/theme/text_input.rs @@ -4,14 +4,14 @@ use iced::widget::text_input; use iced::widget::text_input::Status; use iced::widget::text_input::Style; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_LIGHTER; -use crate::theme::BACKGROUND_LIGHTEST; -use crate::theme::BUTTON_BORDER_RADIUS; -use crate::theme::GauntletSettingsTheme; -use crate::theme::TEXT_DARKER; -use crate::theme::TEXT_LIGHTEST; -use crate::theme::TRANSPARENT; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_LIGHTER; +use crate::ui::settings::theme::BACKGROUND_LIGHTEST; +use crate::ui::settings::theme::BUTTON_BORDER_RADIUS; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::TEXT_DARKER; +use crate::ui::settings::theme::TEXT_LIGHTEST; +use crate::ui::settings::theme::TRANSPARENT; pub enum TextInputStyle { FormInput, diff --git a/rust/management_client/src/ui.rs b/rust/client/src/ui/settings/ui.rs similarity index 67% rename from rust/management_client/src/ui.rs rename to rust/client/src/ui/settings/ui.rs index 2948fcc..18bb420 100644 --- a/rust/management_client/src/ui.rs +++ b/rust/client/src/ui/settings/ui.rs @@ -1,29 +1,23 @@ use std::collections::HashMap; +use std::sync::Arc; use std::time::Duration; use gauntlet_common::model::DownloadStatus; use gauntlet_common::model::EntrypointId; -use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; -use gauntlet_common::model::SettingsTheme; -use gauntlet_common::model::WindowPositionMode; -use gauntlet_common::rpc::backend_api::BackendForSettingsApi; -use gauntlet_common::rpc::backend_api::BackendForSettingsApiProxy; -use gauntlet_common::rpc::backend_api::GrpcBackendApi; use gauntlet_common_ui::padding; +use gauntlet_server::global_hotkey::GlobalHotKeyManager; +use gauntlet_server::plugins::ApplicationManager; use gauntlet_utils::channel::RequestError; use gauntlet_utils::channel::RequestResult; use iced::Alignment; use iced::Length; use iced::Padding; -use iced::Renderer; use iced::Size; use iced::Subscription; use iced::Task; use iced::advanced::text::Shaping; use iced::alignment; -use iced::font; -use iced::futures; use iced::padding; use iced::time; use iced::widget::button; @@ -37,54 +31,73 @@ use iced::widget::scrollable; use iced::widget::stack; use iced::widget::text; use iced::window; -use iced_fonts::BOOTSTRAP_FONT_BYTES; use iced_fonts::bootstrap::exclamation_triangle_fill; use iced_fonts::bootstrap::gear_fill; use iced_fonts::bootstrap::patch_check_fill; use iced_fonts::bootstrap::puzzle_fill; use itertools::Itertools; -use crate::components::spinner::Spinner; -use crate::theme::Element; -use crate::theme::GauntletSettingsTheme; -use crate::theme::button::ButtonStyle; -use crate::theme::container::ContainerStyle; -use crate::theme::text::TextStyle; -use crate::views::general::ManagementAppGeneralMsgIn; -use crate::views::general::ManagementAppGeneralMsgOut; -use crate::views::general::ManagementAppGeneralState; -use crate::views::plugins::ManagementAppPluginMsgIn; -use crate::views::plugins::ManagementAppPluginMsgOut; -use crate::views::plugins::ManagementAppPluginsState; +use crate::ui::settings::components::spinner::Spinner; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::button::ButtonStyle; +use crate::ui::settings::theme::container::ContainerStyle; +use crate::ui::settings::theme::text::TextStyle; +use crate::ui::settings::views::general::SettingsGeneralMsgIn; +use crate::ui::settings::views::general::SettingsGeneralMsgOut; +use crate::ui::settings::views::general::SettingsGeneralState; +use crate::ui::settings::views::plugins::SelectedItem; +use crate::ui::settings::views::plugins::SettingsPluginMsgIn; +use crate::ui::settings::views::plugins::SettingsPluginMsgOut; +use crate::ui::settings::views::plugins::SettingsPluginsState; -pub fn run() { - iced::application::(new, update, view) - .title("Gauntlet Settings") - .window(window::Settings { - size: Size::new(1150.0, 700.0), - ..Default::default() - }) - .subscription(subscription) - .theme(|_| GauntletSettingsTheme::default()) - .run() - .expect("Unable to start settings application"); -} - -struct ManagementAppModel { - backend_api: Option, +pub struct SettingsWindowState { + pub settings_window_id: Option, + application_manager: Arc, + wayland: bool, error_view: Option, downloads_info: HashMap, download_info_shown: bool, current_settings_view: SettingsView, - general_state: ManagementAppGeneralState, - plugins_state: ManagementAppPluginsState, + general_state: SettingsGeneralState, + plugins_state: SettingsPluginsState, +} + +impl SettingsWindowState { + pub fn new(application_manager: Arc, wayland: bool) -> SettingsWindowState { + SettingsWindowState { + settings_window_id: None, + application_manager: application_manager.clone(), + wayland, + error_view: None, + downloads_info: HashMap::new(), + download_info_shown: false, + current_settings_view: SettingsView::Plugins, + general_state: SettingsGeneralState::new(application_manager.clone()), + plugins_state: SettingsPluginsState::new(application_manager.clone()), + } + } +} + +#[derive(Clone, Debug)] +pub enum SettingsParams { + Default, + PluginPreferences { + plugin_id: PluginId, + }, + EntrypointPreferences { + plugin_id: PluginId, + entrypoint_id: EntrypointId, + }, } #[derive(Debug, Clone)] -pub enum ManagementAppMsg { - FontLoaded(Result<(), font::Error>), - General(ManagementAppGeneralMsgIn), - Plugin(ManagementAppPluginMsgIn), +pub enum SettingsMsg { + Refresh, + OpenSettings(SettingsParams), + WindowCreated(window::Id), + WindowDestroyed, + General(SettingsGeneralMsgIn), + Plugin(SettingsPluginMsgIn), SwitchView(SettingsView), DownloadStatus { plugins: HashMap }, HandleBackendError(RequestError), @@ -113,130 +126,34 @@ pub enum DownloadInfo { Successful, } -fn new() -> (ManagementAppModel, Task) { - let backend_api = futures::executor::block_on(async { - anyhow::Ok(BackendForSettingsApiProxy::new(GrpcBackendApi::new().await?)) - }) - .inspect_err(|err| tracing::error!("Unable to connect to server: {:?}", err)) - .ok(); - - let wayland = if cfg!(target_os = "linux") { - std::env::var("WAYLAND_DISPLAY") - .or_else(|_| std::env::var("WAYLAND_SOCKET")) - .is_ok() - } else { - false - }; - - ( - ManagementAppModel { - backend_api: backend_api.clone(), - error_view: None, - downloads_info: HashMap::new(), - download_info_shown: false, - current_settings_view: SettingsView::Plugins, - general_state: ManagementAppGeneralState::new(backend_api.clone()), - plugins_state: ManagementAppPluginsState::new(backend_api.clone()), - }, - Task::batch([ - font::load(BOOTSTRAP_FONT_BYTES).map(ManagementAppMsg::FontLoaded), - Task::done(ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::FetchPlugins)), - Task::future(async { - match backend_api { - Some(backend_api) => Some(init_data(backend_api).await), - None => None, - } - }) - .then(move |init_data| { - match init_data { - None => Task::done(ManagementAppMsg::General(ManagementAppGeneralMsgIn::Noop)), - Some(init) => { - match init { - Ok(init) => { - Task::batch([ - Task::done(ManagementAppMsg::General(ManagementAppGeneralMsgIn::InitSetting { - theme: init.theme, - window_position_mode: init.window_position_mode, - shortcut: init.global_shortcut, - shortcut_error: init.global_shortcut_error, - global_shortcuts_unsupported: wayland && !init.wayland_global_shortcuts_enabled, - })), - Task::done(ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::InitSetting { - global_entrypoint_shortcuts: init.global_entrypoint_shortcuts, - show_global_shortcuts: !wayland || init.wayland_global_shortcuts_enabled, - })), - ]) - } - Err(err) => Task::done(ManagementAppMsg::HandleBackendError(err)), - } - } - } - }), - ]), - ) -} - -struct InitSettingsData { - global_shortcut: Option, - global_shortcut_error: Option, - theme: SettingsTheme, - window_position_mode: WindowPositionMode, - global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, - wayland_global_shortcuts_enabled: bool, -} - -async fn init_data(backend_api: impl BackendForSettingsApi) -> RequestResult { - let (global_shortcut, global_shortcut_error) = backend_api.get_global_shortcut().await?; - let global_entrypoint_shortcuts = backend_api.get_global_entrypoint_shortcuts().await?; - - let theme = backend_api.get_theme().await?; - - let window_position_mode = backend_api.get_window_position_mode().await?; - let wayland_global_shortcuts_enabled = backend_api.wayland_global_shortcuts_enabled().await?; - - Ok(InitSettingsData { - global_shortcut, - global_shortcut_error, - global_entrypoint_shortcuts, - theme, - window_position_mode, - wayland_global_shortcuts_enabled, - }) -} - -fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task { - let backend_api = match &state.backend_api { - Some(backend_api) => backend_api.clone(), - None => return Task::none(), - }; - +pub fn update_settings( + state: &mut SettingsWindowState, + global_hotkey_manager: &Option, + message: SettingsMsg, +) -> Task { match message { - ManagementAppMsg::Plugin(message) => { - state.plugins_state.update(message).map(|msg| { + SettingsMsg::Plugin(message) => { + state.plugins_state.update(global_hotkey_manager, message).map(|msg| { match msg { - ManagementAppPluginMsgOut::Inner(msg) => ManagementAppMsg::Plugin(msg), - ManagementAppPluginMsgOut::Outer(msg) => msg, + SettingsPluginMsgOut::Inner(msg) => SettingsMsg::Plugin(msg), + SettingsPluginMsgOut::Outer(msg) => msg, } }) } - ManagementAppMsg::General(message) => { - state.general_state.update(message).map(|msg| { + SettingsMsg::General(message) => { + state.general_state.update(global_hotkey_manager, message).map(|msg| { match msg { - ManagementAppGeneralMsgOut::Inner(msg) => ManagementAppMsg::General(msg), - ManagementAppGeneralMsgOut::Outer(msg) => msg, + SettingsGeneralMsgOut::Inner(msg) => SettingsMsg::General(msg), + SettingsGeneralMsgOut::Outer(msg) => msg, } }) } - ManagementAppMsg::FontLoaded(result) => { - result.expect("unable to load font"); - Task::none() - } - ManagementAppMsg::SwitchView(view) => { + SettingsMsg::SwitchView(view) => { state.current_settings_view = view; Task::none() } - ManagementAppMsg::HandleBackendError(err) => { + SettingsMsg::HandleBackendError(err) => { state.error_view = Some(match err { RequestError::Timeout => ErrorView::Timeout, RequestError::Other { display } => ErrorView::UnknownError { display }, @@ -249,7 +166,7 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task { + SettingsMsg::DownloadStatus { plugins } => { for (plugin, status) in plugins { match status { DownloadStatus::InProgress => { @@ -266,13 +183,13 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task Task Task { + SettingsMsg::CheckDownloadStatus => { if state.downloads_info.is_empty() { Task::none() } else { - let backend_client = backend_api.clone(); + let plugins = state.application_manager.download_status(); - Task::perform( - async move { - let plugins = backend_client.download_status().await?; - - Ok(plugins) - }, - |result| handle_backend_error(result, |plugins| ManagementAppMsg::DownloadStatus { plugins }), - ) + Task::done(SettingsMsg::DownloadStatus { plugins }) } } - ManagementAppMsg::DownloadPlugin { plugin_id } => { - let backend_client = backend_api.clone(); + SettingsMsg::DownloadPlugin { plugin_id } => { + let backend_client = state.application_manager.clone(); let already_downloading = state .downloads_info @@ -317,39 +227,97 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task Task::none(), - ManagementAppMsg::ToggleDownloadInfo => { + SettingsMsg::Noop => Task::none(), + SettingsMsg::ToggleDownloadInfo => { state.download_info_shown = !state.download_info_shown; Task::none() } + SettingsMsg::Refresh => { + fn run(state: &mut SettingsWindowState) -> anyhow::Result> { + let (global_shortcut, global_shortcut_error) = + state.application_manager.get_global_shortcut().map(|data| { + data.map(|(shortcut, error)| (Some(shortcut), error)) + .unwrap_or((None, None)) + })?; + + let global_entrypoint_shortcuts = state.application_manager.get_global_entrypoint_shortcuts()?; + + let theme = state.application_manager.get_theme()?; + + let window_position_mode = state.application_manager.get_window_position_mode()?; + let wayland_global_shortcuts_enabled = state.application_manager.config()?.wayland_use_legacy_x11_api; + + Ok(Task::batch([ + Task::done(SettingsMsg::General(SettingsGeneralMsgIn::InitSetting { + theme, + window_position_mode, + shortcut: global_shortcut, + shortcut_error: global_shortcut_error, + global_shortcuts_unsupported: state.wayland && !wayland_global_shortcuts_enabled, + })), + Task::done(SettingsMsg::Plugin(SettingsPluginMsgIn::InitSetting { + global_entrypoint_shortcuts, + show_global_shortcuts: !state.wayland || wayland_global_shortcuts_enabled, + })), + ])) + } + + run(state).unwrap_or_else(|err| Task::done(SettingsMsg::HandleBackendError(err.into()))) + } + SettingsMsg::WindowCreated(window_id) => { + state.settings_window_id = Some(window_id); + + Task::none() + } + SettingsMsg::WindowDestroyed => { + state.settings_window_id = None; + + Task::none() + } + SettingsMsg::OpenSettings(settings_params) => { + let item = match settings_params { + SettingsParams::Default => SelectedItem::None, + SettingsParams::PluginPreferences { plugin_id } => SelectedItem::Plugin { plugin_id }, + SettingsParams::EntrypointPreferences { + plugin_id, + entrypoint_id, + } => { + SelectedItem::Entrypoint { + plugin_id, + entrypoint_id, + } + } + }; + + let open = match state.settings_window_id { + None => { + let settings = window::Settings { + size: Size::new(1150.0, 700.0), + ..Default::default() + }; + let (_, open) = window::open(settings); + open.map(SettingsMsg::WindowCreated) + } + Some(window_id) => window::gain_focus(window_id), + }; + + Task::batch([ + open, + Task::done(SettingsMsg::Plugin(SettingsPluginMsgIn::FetchPlugins)), + Task::done(SettingsMsg::Refresh), + Task::done(SettingsMsg::SwitchView(SettingsView::Plugins)), + Task::done(SettingsMsg::Plugin(SettingsPluginMsgIn::SelectItem(item))), + ]) + } } } -fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { - if let None = &state.backend_api { - let description: Element<_> = - text("Unable to connect to server. Please check if you have Gauntlet running on your PC").into(); - - let content: Element<_> = container(description) - .align_x(Alignment::Center) - .align_y(Alignment::Center) - .width(Length::Fill) - .height(Length::Fill) - .into(); - - return content; - } - +pub fn view_settings(state: &SettingsWindowState) -> Element<'_, SettingsMsg> { if let Some(err) = &state.error_view { return match err { ErrorView::Timeout => { @@ -421,8 +389,8 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { } let content = match state.current_settings_view { - SettingsView::General => state.general_state.view().map(|msg| ManagementAppMsg::General(msg)), - SettingsView::Plugins => state.plugins_state.view().map(|msg| ManagementAppMsg::Plugin(msg)), + SettingsView::General => state.general_state.view().map(|msg| SettingsMsg::General(msg)), + SettingsView::Plugins => state.plugins_state.view().map(|msg| SettingsMsg::Plugin(msg)), }; let icon_general: Element<_> = gear_fill() @@ -445,7 +413,7 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { .into(); let general_button: Element<_> = button(general_button) - .on_press(ManagementAppMsg::SwitchView(SettingsView::General)) + .on_press(SettingsMsg::SwitchView(SettingsView::General)) .height(Length::Fill) .width(80) .class( @@ -479,7 +447,7 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { .into(); let plugins_button: Element<_> = button(plugins_button) - .on_press(ManagementAppMsg::SwitchView(SettingsView::Plugins)) + .on_press(SettingsMsg::SwitchView(SettingsView::Plugins)) .height(Length::Fill) .width(80) .class( @@ -587,7 +555,7 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { let top_bar_right: Element<_> = button(top_bar_right) .class(ButtonStyle::DownloadInfo) - .on_press(ManagementAppMsg::ToggleDownloadInfo) + .on_press(SettingsMsg::ToggleDownloadInfo) .padding(Padding::from([4, 8])) .height(Length::Fill) .into(); @@ -735,12 +703,17 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { .into() }; + let content = container(content) + .width(Length::Fill) + .height(Length::Fill) + .class(ContainerStyle::WindowRoot); + let content: Element<_> = mouse_area(content) .on_press( if state.download_info_shown { - ManagementAppMsg::ToggleDownloadInfo + SettingsMsg::ToggleDownloadInfo } else { - ManagementAppMsg::Noop + SettingsMsg::Noop }, ) .into(); @@ -754,16 +727,16 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { stack(content).into() } -fn subscription(_state: &ManagementAppModel) -> Subscription { - time::every(Duration::from_millis(300)).map(|_| ManagementAppMsg::CheckDownloadStatus) -} - -pub fn handle_backend_error( - result: RequestResult, - convert: impl FnOnce(T) -> ManagementAppMsg, -) -> ManagementAppMsg { - match result { - Ok(val) => convert(val), - Err(err) => ManagementAppMsg::HandleBackendError(err), +pub fn subscription_settings(state: &SettingsWindowState) -> Subscription { + match state.settings_window_id { + None => Subscription::none(), + Some(_) => time::every(Duration::from_millis(300)).map(|_| SettingsMsg::CheckDownloadStatus), + } +} + +pub fn handle_backend_error(result: RequestResult, convert: impl FnOnce(T) -> SettingsMsg) -> SettingsMsg { + match result { + Ok(val) => convert(val), + Err(err) => SettingsMsg::HandleBackendError(err), } } diff --git a/rust/management_client/src/views/general.rs b/rust/client/src/ui/settings/views/general.rs similarity index 66% rename from rust/management_client/src/views/general.rs rename to rust/client/src/ui/settings/views/general.rs index 5679bd0..19179eb 100644 --- a/rust/management_client/src/views/general.rs +++ b/rust/client/src/ui/settings/views/general.rs @@ -1,8 +1,10 @@ +use std::sync::Arc; + use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::SettingsTheme; use gauntlet_common::model::WindowPositionMode; -use gauntlet_common::rpc::backend_api::BackendForSettingsApi; -use gauntlet_common::rpc::backend_api::BackendForSettingsApiProxy; +use gauntlet_server::global_hotkey::GlobalHotKeyManager; +use gauntlet_server::plugins::ApplicationManager; use gauntlet_utils::channel::RequestResult; use iced::Alignment; use iced::Font; @@ -20,15 +22,15 @@ use iced::widget::row; use iced::widget::text; use iced::widget::text::Shaping; -use crate::components::shortcut_selector::ShortcutData; -use crate::components::shortcut_selector::render_shortcut_error; -use crate::components::shortcut_selector::shortcut_selector; -use crate::theme::Element; -use crate::theme::container::ContainerStyle; -use crate::ui::ManagementAppMsg; +use crate::ui::settings::components::shortcut_selector::ShortcutData; +use crate::ui::settings::components::shortcut_selector::render_shortcut_error; +use crate::ui::settings::components::shortcut_selector::shortcut_selector; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::container::ContainerStyle; +use crate::ui::settings::ui::SettingsMsg; -pub struct ManagementAppGeneralState { - backend_api: Option, +pub struct SettingsGeneralState { + application_manager: Arc, theme: SettingsTheme, window_position_mode: WindowPositionMode, current_shortcut: ShortcutData, @@ -36,7 +38,7 @@ pub struct ManagementAppGeneralState { } #[derive(Debug, Clone)] -pub enum ManagementAppGeneralMsgIn { +pub enum SettingsGeneralMsgIn { ShortcutCaptured(Option), ThemeChanged(SettingsTheme), WindowPositionModeChanged(WindowPositionMode), @@ -51,19 +53,18 @@ pub enum ManagementAppGeneralMsgIn { shortcut_error: Option, global_shortcuts_unsupported: bool, }, - Noop, } #[derive(Debug, Clone)] -pub enum ManagementAppGeneralMsgOut { - Inner(ManagementAppGeneralMsgIn), - Outer(ManagementAppMsg), +pub enum SettingsGeneralMsgOut { + Inner(SettingsGeneralMsgIn), + Outer(SettingsMsg), } -impl ManagementAppGeneralState { - pub fn new(backend_api: Option) -> Self { +impl SettingsGeneralState { + pub fn new(application_manager: Arc) -> Self { Self { - backend_api, + application_manager, theme: SettingsTheme::AutoDetect, window_position_mode: WindowPositionMode::Static, current_shortcut: ShortcutData { @@ -74,40 +75,29 @@ impl ManagementAppGeneralState { } } - pub fn update(&mut self, message: ManagementAppGeneralMsgIn) -> Task { - let backend_api = match &self.backend_api { - Some(backend_api) => backend_api.clone(), - None => return Task::none(), - }; - + pub fn update( + &mut self, + global_hotkey_manager: &Option, + message: SettingsGeneralMsgIn, + ) -> Task { match message { - ManagementAppGeneralMsgIn::ShortcutCaptured(shortcut) => { - let backend_api = backend_api.clone(); + SettingsGeneralMsgIn::ShortcutCaptured(shortcut) => { + let Some(global_hotkey_manager) = &global_hotkey_manager else { + return Task::none(); + }; - Task::perform( - { - let shortcut = shortcut.clone(); + let error = self + .application_manager + .set_global_shortcut(global_hotkey_manager, shortcut.clone()); - async move { - let error = backend_api.set_global_shortcut(shortcut).await?; - - Ok(error) - } + Task::done(SettingsGeneralMsgOut::Inner( + SettingsGeneralMsgIn::HandleShortcutResponse { + shortcut, + shortcut_error: error, }, - move |result| { - let shortcut = shortcut.clone(); - - handle_backend_error(result, move |shortcut_error| { - ManagementAppGeneralMsgOut::Inner(ManagementAppGeneralMsgIn::HandleShortcutResponse { - shortcut, - shortcut_error, - }) - }) - }, - ) + )) } - ManagementAppGeneralMsgIn::Noop => Task::none(), - ManagementAppGeneralMsgIn::InitSetting { + SettingsGeneralMsgIn::InitSetting { theme, window_position_mode, shortcut, @@ -124,39 +114,35 @@ impl ManagementAppGeneralState { Task::none() } - ManagementAppGeneralMsgIn::ThemeChanged(theme) => { + SettingsGeneralMsgIn::ThemeChanged(theme) => { self.theme = theme.clone(); - let backend_api = backend_api.clone(); + let application_manager = self.application_manager.clone(); Task::perform( async move { - backend_api.set_theme(theme).await?; + application_manager.set_theme(theme).await?; Ok(()) }, - |result| { - handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::Noop)) - }, + |result| handle_backend_error(result, |()| SettingsGeneralMsgOut::Outer(SettingsMsg::Noop)), ) } - ManagementAppGeneralMsgIn::WindowPositionModeChanged(mode) => { + SettingsGeneralMsgIn::WindowPositionModeChanged(mode) => { self.window_position_mode = mode.clone(); - let backend_api = backend_api.clone(); + let application_manager = self.application_manager.clone(); Task::perform( async move { - backend_api.set_window_position_mode(mode).await?; + application_manager.set_window_position_mode(mode).await?; Ok(()) }, - |result| { - handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::Noop)) - }, + |result| handle_backend_error(result, |()| SettingsGeneralMsgOut::Outer(SettingsMsg::Noop)), ) } - ManagementAppGeneralMsgIn::HandleShortcutResponse { + SettingsGeneralMsgIn::HandleShortcutResponse { shortcut, shortcut_error, } => { @@ -170,7 +156,7 @@ impl ManagementAppGeneralState { } } - pub fn view(&self) -> Element { + pub fn view(&self) -> Element { let global_shortcut_selector: Element<_> = if self.global_shortcuts_unsupported { let text = text("Not supported").font(Font { style: Style::Italic, @@ -186,7 +172,7 @@ impl ManagementAppGeneralState { } else { shortcut_selector( &self.current_shortcut, - move |shortcut| ManagementAppGeneralMsgIn::ShortcutCaptured(shortcut), + move |shortcut| SettingsGeneralMsgIn::ShortcutCaptured(shortcut), ContainerStyle::Box, false, ) @@ -221,7 +207,7 @@ impl ManagementAppGeneralState { content } - fn theme_field(&self) -> Element { + fn theme_field(&self) -> Element { let theme_field = match &self.theme { SettingsTheme::ThemeFile => { let theme_field: Element<_> = text("Unable to change because theme config file is present ") @@ -250,7 +236,7 @@ impl ManagementAppGeneralState { ]; let theme_field: Element<_> = pick_list(theme_items, Some(self.theme.clone()), move |item| { - ManagementAppGeneralMsgIn::ThemeChanged(item) + SettingsGeneralMsgIn::ThemeChanged(item) }) .into(); @@ -266,11 +252,11 @@ impl ManagementAppGeneralState { } #[allow(unused)] - fn window_position_mode_field(&self) -> Element { + fn window_position_mode_field(&self) -> Element { let items = [WindowPositionMode::Static, WindowPositionMode::ActiveMonitor]; let field: Element<_> = pick_list(items, Some(self.window_position_mode.clone()), move |item| { - ManagementAppGeneralMsgIn::WindowPositionModeChanged(item) + SettingsGeneralMsgIn::WindowPositionModeChanged(item) }) .into(); @@ -284,9 +270,9 @@ impl ManagementAppGeneralState { fn view_field<'a>( &'a self, label: &'a str, - input: Element<'a, ManagementAppGeneralMsgIn>, - after: Option>, - ) -> Element<'a, ManagementAppGeneralMsgIn> { + input: Element<'a, SettingsGeneralMsgIn>, + after: Option>, + ) -> Element<'a, SettingsGeneralMsgIn> { let label: Element<_> = text(label) .shaping(Shaping::Advanced) .align_x(Horizontal::Right) @@ -306,7 +292,7 @@ impl ManagementAppGeneralState { row } - fn shortcut_capture_after(&self) -> Element { + fn shortcut_capture_after(&self) -> Element { if let Some(current_shortcut_error) = &self.current_shortcut.error { let content = render_shortcut_error(current_shortcut_error.clone()); @@ -325,10 +311,10 @@ impl ManagementAppGeneralState { fn handle_backend_error( result: RequestResult, - convert: impl FnOnce(T) -> ManagementAppGeneralMsgOut, -) -> ManagementAppGeneralMsgOut { + convert: impl FnOnce(T) -> SettingsGeneralMsgOut, +) -> SettingsGeneralMsgOut { match result { Ok(val) => convert(val), - Err(err) => ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::HandleBackendError(err)), + Err(err) => SettingsGeneralMsgOut::Outer(SettingsMsg::HandleBackendError(err)), } } diff --git a/rust/management_client/src/views/mod.rs b/rust/client/src/ui/settings/views/mod.rs similarity index 100% rename from rust/management_client/src/views/mod.rs rename to rust/client/src/ui/settings/views/mod.rs diff --git a/rust/management_client/src/views/plugins.rs b/rust/client/src/ui/settings/views/plugins.rs similarity index 66% rename from rust/management_client/src/views/plugins.rs rename to rust/client/src/ui/settings/views/plugins.rs index 3132904..551f4d5 100644 --- a/rust/management_client/src/views/plugins.rs +++ b/rust/client/src/ui/settings/views/plugins.rs @@ -1,18 +1,16 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; +use std::sync::Arc; -use gauntlet_common::SETTINGS_ENV; -use gauntlet_common::SettingsEnvData; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; use gauntlet_common::model::PluginPreferenceUserData; use gauntlet_common::model::SettingsEntrypointType; use gauntlet_common::model::SettingsPlugin; -use gauntlet_common::rpc::backend_api::BackendForSettingsApi; -use gauntlet_common::rpc::backend_api::BackendForSettingsApiProxy; -use gauntlet_common::settings_env_data_from_string; +use gauntlet_server::global_hotkey::GlobalHotKeyManager; +use gauntlet_server::plugins::ApplicationManager; use gauntlet_utils::channel::RequestResult; use iced::Alignment; use iced::Length; @@ -30,22 +28,22 @@ use iced::widget::text_input; use iced::widget::vertical_rule; use iced_fonts::bootstrap::plus; -use crate::theme::Element; -use crate::theme::button::ButtonStyle; -use crate::theme::text::TextStyle; -use crate::ui::ManagementAppMsg; -use crate::views::plugins::preferences::PluginPreferencesMsg; -use crate::views::plugins::preferences::SelectItem; -use crate::views::plugins::preferences::preferences_ui; -use crate::views::plugins::table::PluginTableMsgIn; -use crate::views::plugins::table::PluginTableMsgOut; -use crate::views::plugins::table::PluginTableState; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::button::ButtonStyle; +use crate::ui::settings::theme::text::TextStyle; +use crate::ui::settings::ui::SettingsMsg; +use crate::ui::settings::views::plugins::preferences::PluginPreferencesMsg; +use crate::ui::settings::views::plugins::preferences::SelectItem; +use crate::ui::settings::views::plugins::preferences::preferences_ui; +use crate::ui::settings::views::plugins::table::PluginTableMsgIn; +use crate::ui::settings::views::plugins::table::PluginTableMsgOut; +use crate::ui::settings::views::plugins::table::PluginTableState; mod preferences; mod table; #[derive(Debug, Clone)] -pub enum ManagementAppPluginMsgIn { +pub enum SettingsPluginMsgIn { InitSetting { global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, show_global_shortcuts: bool, @@ -74,13 +72,13 @@ pub enum ManagementAppPluginMsgIn { SelectItem(SelectedItem), } -pub enum ManagementAppPluginMsgOut { - Inner(ManagementAppPluginMsgIn), - Outer(ManagementAppMsg), +pub enum SettingsPluginMsgOut { + Inner(SettingsPluginMsgIn), + Outer(SettingsMsg), } -pub struct ManagementAppPluginsState { - backend_api: Option, +pub struct SettingsPluginsState { + application_manager: Arc, table_state: PluginTableState, plugin_data: Rc>, preference_user_data: HashMap<(PluginId, Option, String), PluginPreferenceUserDataState>, @@ -89,52 +87,27 @@ pub struct ManagementAppPluginsState { entrypoint_search_aliases: HashMap<(PluginId, EntrypointId), String>, } -impl ManagementAppPluginsState { - pub fn new(backend_api: Option) -> Self { - let settings_env_data = std::env::var(SETTINGS_ENV) - .ok() - .filter(|value| !value.is_empty()) - .map(|val| settings_env_data_from_string(val)); - - let select_item = match settings_env_data { - None => SelectedItem::None, - Some(SettingsEnvData::OpenEntrypointPreferences { - plugin_id, - entrypoint_id, - }) => { - SelectedItem::Entrypoint { - plugin_id: PluginId::from_string(plugin_id), - entrypoint_id: EntrypointId::from_string(entrypoint_id), - } - } - Some(SettingsEnvData::OpenPluginPreferences { plugin_id }) => { - SelectedItem::Plugin { - plugin_id: PluginId::from_string(plugin_id), - } - } - }; - - tracing::debug!("Opening selected item: {:?}", select_item); - +impl SettingsPluginsState { + pub fn new(application_manager: Arc) -> Self { Self { - backend_api: backend_api.clone(), + application_manager: application_manager.clone(), plugin_data: Rc::new(RefCell::new(PluginDataContainer::new())), preference_user_data: HashMap::new(), - selected_item: select_item, + selected_item: SelectedItem::None, table_state: PluginTableState::new(), global_entrypoint_shortcuts: HashMap::new(), entrypoint_search_aliases: HashMap::new(), } } - pub fn update(&mut self, message: ManagementAppPluginMsgIn) -> Task { - let backend_api = match &self.backend_api { - Some(backend_api) => backend_api.clone(), - None => return Task::none(), - }; - + pub fn update( + &mut self, + global_hotkey_manager: &Option, + message: SettingsPluginMsgIn, + ) -> Task { + let application_manager = self.application_manager.clone(); match message { - ManagementAppPluginMsgIn::InitSetting { + SettingsPluginMsgIn::InitSetting { global_entrypoint_shortcuts, show_global_shortcuts, } => { @@ -143,156 +116,162 @@ impl ManagementAppPluginsState { Task::none() } - ManagementAppPluginMsgIn::PluginTableMsg(message) => { - self.table_state.update(message).then(move |msg| { - match msg { - PluginTableMsgOut::SetPluginState { enabled, plugin_id } => { - let backend_client = backend_api.clone(); + SettingsPluginMsgIn::PluginTableMsg(message) => { + let application_manager = application_manager.clone(); + match self.table_state.update(message) { + PluginTableMsgOut::SetPluginState { enabled, plugin_id } => { + let application_manager = application_manager.clone(); - Task::perform( - async move { - backend_client.set_plugin_state(plugin_id, enabled).await?; + Task::perform( + async move { + application_manager.set_plugin_state(plugin_id, enabled)?; - let plugins = backend_client.plugins().await?; - let global_entrypoint_shortcuts = - backend_client.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?; + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = + application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; - Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) - }, - |result| { - handle_backend_error( - result, - |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( - plugins, - global_entrypoint_shortcuts, - entrypoint_aliases, - )) - }, - ) - }, - ) - } - PluginTableMsgOut::SetEntrypointState { - enabled, + Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) + }, + |result| { + handle_backend_error( + result, + |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { + SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( + plugins, + global_entrypoint_shortcuts, + entrypoint_aliases, + )) + }, + ) + }, + ) + } + PluginTableMsgOut::SetEntrypointState { + enabled, + plugin_id, + entrypoint_id, + } => { + let application_manager = application_manager.clone(); + + Task::perform( + async move { + application_manager.set_entrypoint_state(plugin_id, entrypoint_id, enabled)?; + + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = + application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; + + Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) + }, + |result| { + handle_backend_error( + result, + |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { + SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( + plugins, + global_entrypoint_shortcuts, + entrypoint_aliases, + )) + }, + ) + }, + ) + } + PluginTableMsgOut::SelectItem(selected_item) => { + Task::done(SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::SelectItem( + selected_item, + ))) + } + PluginTableMsgOut::ToggleShowEntrypoints { plugin_id } => { + Task::done(SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::ToggleShowEntrypoint { plugin_id, - entrypoint_id, - } => { - let backend_client = backend_api.clone(); + })) + } + PluginTableMsgOut::ToggleShowGeneratedEntrypoints { + plugin_id, + entrypoint_id, + } => { + Task::done(SettingsPluginMsgOut::Inner( + SettingsPluginMsgIn::ToggleShowGeneratedEntrypoint { + plugin_id, + entrypoint_id, + }, + )) + } + PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) => { + let Some(global_hotkey_manager) = &global_hotkey_manager else { + return Task::none(); + }; - Task::perform( - async move { - backend_client - .set_entrypoint_state(plugin_id, entrypoint_id, enabled) - .await?; + fn run( + application_manager: &ApplicationManager, + global_hotkey_manager: &GlobalHotKeyManager, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + ) -> anyhow::Result { + application_manager.set_global_entrypoint_shortcut( + global_hotkey_manager, + plugin_id, + entrypoint_id, + shortcut, + )?; - let plugins = backend_client.plugins().await?; - let global_entrypoint_shortcuts = - backend_client.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?; + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; - Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) - }, - |result| { - handle_backend_error( - result, - |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( - plugins, - global_entrypoint_shortcuts, - entrypoint_aliases, - )) - }, - ) - }, - ) - } - PluginTableMsgOut::SelectItem(selected_item) => { - Task::done(ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::SelectItem( - selected_item, + Ok(SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( + plugins, + global_entrypoint_shortcuts, + entrypoint_aliases, ))) } - PluginTableMsgOut::ToggleShowEntrypoints { plugin_id } => { - Task::done(ManagementAppPluginMsgOut::Inner( - ManagementAppPluginMsgIn::ToggleShowEntrypoint { plugin_id }, - )) - } - PluginTableMsgOut::ToggleShowGeneratedEntrypoints { + + let msg_out = run( + &application_manager, + global_hotkey_manager, plugin_id, entrypoint_id, - } => { - Task::done(ManagementAppPluginMsgOut::Inner( - ManagementAppPluginMsgIn::ToggleShowGeneratedEntrypoint { - plugin_id, - entrypoint_id, - }, - )) - } - PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) => { - let backend_client = backend_api.clone(); + shortcut, + ) + .unwrap_or_else(|err| SettingsPluginMsgOut::Outer(SettingsMsg::HandleBackendError(err.into()))); - Task::perform( - async move { - backend_client - .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) - .await?; - - let plugins = backend_client.plugins().await?; - let global_entrypoint_shortcuts = - backend_client.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?; - - Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) - }, - |result| { - handle_backend_error( - result, - |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( - plugins, - global_entrypoint_shortcuts, - entrypoint_aliases, - )) - }, - ) - }, - ) - } - PluginTableMsgOut::AliasChanged(plugin_id, entrypoint_id, shortcut) => { - let backend_client = backend_api.clone(); - - Task::perform( - async move { - backend_client - .set_entrypoint_search_alias(plugin_id, entrypoint_id, shortcut) - .await?; - - let plugins = backend_client.plugins().await?; - let global_entrypoint_shortcuts = - backend_client.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?; - - Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) - }, - |result| { - handle_backend_error( - result, - |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( - plugins, - global_entrypoint_shortcuts, - entrypoint_aliases, - )) - }, - ) - }, - ) - } + Task::done(msg_out) } - }) + PluginTableMsgOut::AliasChanged(plugin_id, entrypoint_id, shortcut) => { + let application_manager = application_manager.clone(); + + Task::perform( + async move { + application_manager.set_entrypoint_search_alias(plugin_id, entrypoint_id, shortcut)?; + + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = + application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; + + Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) + }, + |result| { + handle_backend_error( + result, + |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { + SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( + plugins, + global_entrypoint_shortcuts, + entrypoint_aliases, + )) + }, + ) + }, + ) + } + } } - ManagementAppPluginMsgIn::ToggleShowEntrypoint { plugin_id } => { + SettingsPluginMsgIn::ToggleShowEntrypoint { plugin_id } => { let plugins = { let mut plugin_data = self.plugin_data.borrow_mut(); let settings_plugin_data = plugin_data.plugins_state.get_mut(&plugin_id).unwrap(); @@ -309,7 +288,7 @@ impl ManagementAppPluginsState { Task::none() } - ManagementAppPluginMsgIn::ToggleShowGeneratedEntrypoint { + SettingsPluginMsgIn::ToggleShowGeneratedEntrypoint { plugin_id, entrypoint_id, } => { @@ -334,7 +313,7 @@ impl ManagementAppPluginsState { Task::none() } - ManagementAppPluginMsgIn::PluginPreferenceMsg(msg) => { + SettingsPluginMsgIn::PluginPreferenceMsg(msg) => { match msg { PluginPreferencesMsg::UpdatePreferenceValue { plugin_id, @@ -347,39 +326,38 @@ impl ManagementAppPluginsState { user_data.clone(), ); - let backend_api = backend_api.clone(); + let application_manager = application_manager.clone(); Task::perform( async move { - backend_api - .set_preference_value(plugin_id, entrypoint_id, id, user_data.to_user_data()) - .await?; + application_manager.set_preference_value( + plugin_id, + entrypoint_id, + id, + user_data.to_user_data(), + )?; Ok(()) }, - |result| { - handle_backend_error(result, |()| { - ManagementAppPluginMsgOut::Outer(ManagementAppMsg::Noop) - }) - }, + |result| handle_backend_error(result, |()| SettingsPluginMsgOut::Outer(SettingsMsg::Noop)), ) } } } - ManagementAppPluginMsgIn::FetchPlugins => { - let backend_api = backend_api.clone(); + SettingsPluginMsgIn::FetchPlugins => { + let application_manager = self.application_manager.clone(); Task::perform( async move { - let plugins = backend_api.plugins().await?; - let global_entrypoint_shortcuts = backend_api.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_api.get_entrypoint_search_aliases().await?; + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) }, |result| { handle_backend_error(result, |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( + SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( plugins, global_entrypoint_shortcuts, entrypoint_aliases, @@ -388,29 +366,29 @@ impl ManagementAppPluginsState { }, ) } - ManagementAppPluginMsgIn::PluginsReloaded(plugins, shortcuts, entrypoint_aliases) => { + SettingsPluginMsgIn::PluginsReloaded(plugins, shortcuts, entrypoint_aliases) => { self.apply_plugin_fetch(plugins, shortcuts, entrypoint_aliases); Task::none() } - ManagementAppPluginMsgIn::RemovePlugin { plugin_id } => { + SettingsPluginMsgIn::RemovePlugin { plugin_id } => { self.selected_item = SelectedItem::None; - let backend_client = backend_api.clone(); + let application_manager = application_manager.clone(); Task::perform( async move { - backend_client.remove_plugin(plugin_id).await?; + application_manager.remove_plugin(plugin_id)?; - let plugins = backend_client.plugins().await?; - let global_entrypoint_shortcuts = backend_client.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?; + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) }, |result| { handle_backend_error(result, |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( + SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( plugins, global_entrypoint_shortcuts, entrypoint_aliases, @@ -419,12 +397,10 @@ impl ManagementAppPluginsState { }, ) } - ManagementAppPluginMsgIn::DownloadPlugin { plugin_id } => { - Task::done(ManagementAppPluginMsgOut::Outer(ManagementAppMsg::DownloadPlugin { - plugin_id, - })) + SettingsPluginMsgIn::DownloadPlugin { plugin_id } => { + Task::done(SettingsPluginMsgOut::Outer(SettingsMsg::DownloadPlugin { plugin_id })) } - ManagementAppPluginMsgIn::SelectItem(selected_item) => { + SettingsPluginMsgIn::SelectItem(selected_item) => { self.selected_item = selected_item; Task::none() @@ -522,11 +498,11 @@ impl ManagementAppPluginsState { ) } - pub fn view(&self) -> Element { + pub fn view(&self) -> Element { let table: Element<_> = self .table_state .view() - .map(|msg| ManagementAppPluginMsgIn::PluginTableMsg(msg)); + .map(|msg| SettingsPluginMsgIn::PluginTableMsg(msg)); let table: Element<_> = container(table).padding(Padding::new(8.0)).into(); @@ -593,7 +569,7 @@ impl ManagementAppPluginsState { column_content.push( preferences_ui(plugin_id.clone(), None, &plugin.preferences, &self.preference_user_data) - .map(|msg| ManagementAppPluginMsgIn::PluginPreferenceMsg(msg)), + .map(|msg| SettingsPluginMsgIn::PluginPreferenceMsg(msg)), ); let content: Element<_> = column(column_content).spacing(12).into(); @@ -614,7 +590,7 @@ impl ManagementAppPluginsState { let check_for_updates_button: Element<_> = button(check_for_updates_text_container) .width(Length::Fill) .class(ButtonStyle::Primary) - .on_press(ManagementAppPluginMsgIn::DownloadPlugin { + .on_press(SettingsPluginMsgIn::DownloadPlugin { plugin_id: plugin.plugin_id.clone(), }) .into(); @@ -632,7 +608,7 @@ impl ManagementAppPluginsState { let remove_button: Element<_> = button(remove_button_text_container) .width(Length::Fill) .class(ButtonStyle::Destructive) - .on_press(ManagementAppPluginMsgIn::RemovePlugin { + .on_press(SettingsPluginMsgIn::RemovePlugin { plugin_id: plugin.plugin_id.clone(), }) .into(); @@ -699,7 +675,7 @@ impl ManagementAppPluginsState { &entrypoint.preferences, &self.preference_user_data, ) - .map(|msg| ManagementAppPluginMsgIn::PluginPreferenceMsg(msg)), + .map(|msg| SettingsPluginMsgIn::PluginPreferenceMsg(msg)), ); let column: Element<_> = column(column_content).spacing(12).into(); @@ -717,9 +693,9 @@ impl ManagementAppPluginsState { SelectedItem::NewPlugin { repository_url } => { let url_input: Element<_> = text_input("Enter Git Repository URL", &repository_url) .on_input(|value| { - ManagementAppPluginMsgIn::SelectItem(SelectedItem::NewPlugin { repository_url: value }) + SettingsPluginMsgIn::SelectItem(SelectedItem::NewPlugin { repository_url: value }) }) - .on_submit(ManagementAppPluginMsgIn::DownloadPlugin { + .on_submit(SettingsPluginMsgIn::DownloadPlugin { plugin_id: PluginId::from_string(repository_url), }) .into(); @@ -805,12 +781,12 @@ impl ManagementAppPluginsState { let top_button_action = match plugin_url { Some(plugin_url) => { - ManagementAppPluginMsgIn::DownloadPlugin { + SettingsPluginMsgIn::DownloadPlugin { plugin_id: PluginId::from_string(plugin_url), } } None => { - ManagementAppPluginMsgIn::SelectItem(SelectedItem::NewPlugin { + SettingsPluginMsgIn::SelectItem(SelectedItem::NewPlugin { repository_url: Default::default(), }) } @@ -960,10 +936,10 @@ impl PluginPreferenceUserDataState { pub fn handle_backend_error( result: RequestResult, - convert: impl FnOnce(T) -> ManagementAppPluginMsgOut, -) -> ManagementAppPluginMsgOut { + convert: impl FnOnce(T) -> SettingsPluginMsgOut, +) -> SettingsPluginMsgOut { match result { Ok(val) => convert(val), - Err(err) => ManagementAppPluginMsgOut::Outer(ManagementAppMsg::HandleBackendError(err)), + Err(err) => SettingsPluginMsgOut::Outer(SettingsMsg::HandleBackendError(err)), } } diff --git a/rust/management_client/src/views/plugins/preferences.rs b/rust/client/src/ui/settings/views/plugins/preferences.rs similarity index 98% rename from rust/management_client/src/views/plugins/preferences.rs rename to rust/client/src/ui/settings/views/plugins/preferences.rs index 103519d..d1c8dd6 100644 --- a/rust/management_client/src/views/plugins/preferences.rs +++ b/rust/client/src/ui/settings/views/plugins/preferences.rs @@ -19,11 +19,11 @@ use iced::widget::text_input; use iced_fonts::bootstrap::dash; use iced_fonts::bootstrap::plus; -use crate::theme::Element; -use crate::theme::button::ButtonStyle; -use crate::theme::container::ContainerStyle; -use crate::theme::text::TextStyle; -use crate::views::plugins::PluginPreferenceUserDataState; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::button::ButtonStyle; +use crate::ui::settings::theme::container::ContainerStyle; +use crate::ui::settings::theme::text::TextStyle; +use crate::ui::settings::views::plugins::PluginPreferenceUserDataState; #[derive(Debug, Clone)] pub enum PluginPreferencesMsg { diff --git a/rust/management_client/src/views/plugins/table.rs b/rust/client/src/ui/settings/views/plugins/table.rs similarity index 95% rename from rust/management_client/src/views/plugins/table.rs rename to rust/client/src/ui/settings/views/plugins/table.rs index b9ca674..803d3b2 100644 --- a/rust/management_client/src/views/plugins/table.rs +++ b/rust/client/src/ui/settings/views/plugins/table.rs @@ -9,7 +9,6 @@ use gauntlet_common::model::SettingsEntrypointType; use gauntlet_common::model::SettingsPlugin; use iced::Alignment; use iced::Length; -use iced::Task; use iced::advanced::text::Shaping; use iced::padding; use iced::widget::Space; @@ -25,15 +24,15 @@ use iced::widget::text_input; use iced_fonts::bootstrap::caret_down; use iced_fonts::bootstrap::caret_right; -use crate::components::shortcut_selector::ShortcutData; -use crate::components::shortcut_selector::shortcut_selector; -use crate::theme::Element; -use crate::theme::button::ButtonStyle; -use crate::theme::container::ContainerStyle; -use crate::theme::text_input::TextInputStyle; -use crate::views::plugins::PluginDataContainer; -use crate::views::plugins::SelectedItem; -use crate::views::plugins::SettingsPluginData; +use crate::ui::settings::components::shortcut_selector::ShortcutData; +use crate::ui::settings::components::shortcut_selector::shortcut_selector; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::button::ButtonStyle; +use crate::ui::settings::theme::container::ContainerStyle; +use crate::ui::settings::theme::text_input::TextInputStyle; +use crate::ui::settings::views::plugins::PluginDataContainer; +use crate::ui::settings::views::plugins::SelectedItem; +use crate::ui::settings::views::plugins::SettingsPluginData; #[derive(Debug, Clone)] pub enum PluginTableMsgIn { @@ -85,47 +84,47 @@ impl PluginTableState { } } - pub fn update(&mut self, message: PluginTableMsgIn) -> Task { + pub fn update(&mut self, message: PluginTableMsgIn) -> PluginTableMsgOut { match message { PluginTableMsgIn::EnabledToggleItem(item) => { match item { EnabledItem::Plugin { enabled, plugin_id } => { - Task::done(PluginTableMsgOut::SetPluginState { enabled, plugin_id }) + PluginTableMsgOut::SetPluginState { enabled, plugin_id } } EnabledItem::Entrypoint { enabled, plugin_id, entrypoint_id, } => { - Task::done(PluginTableMsgOut::SetEntrypointState { + PluginTableMsgOut::SetEntrypointState { enabled, plugin_id, entrypoint_id, - }) + } } } } - PluginTableMsgIn::SelectItem(item) => Task::done(PluginTableMsgOut::SelectItem(item)), + PluginTableMsgIn::SelectItem(item) => PluginTableMsgOut::SelectItem(item), PluginTableMsgIn::ToggleShowEntrypoints { plugin_id } => { - Task::done(PluginTableMsgOut::ToggleShowEntrypoints { plugin_id }) + PluginTableMsgOut::ToggleShowEntrypoints { plugin_id } } PluginTableMsgIn::ToggleShowGeneratedEntrypoints { plugin_id, entrypoint_id, } => { - Task::done(PluginTableMsgOut::ToggleShowGeneratedEntrypoints { + PluginTableMsgOut::ToggleShowGeneratedEntrypoints { plugin_id, entrypoint_id, - }) + } } PluginTableMsgIn::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) => { - Task::done(PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut)) + PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) } PluginTableMsgIn::AliasChanged(plugin_id, entrypoint_id, alias) => { let alias = alias.trim().to_owned(); let alias = if alias.is_empty() { None } else { Some(alias) }; - Task::done(PluginTableMsgOut::AliasChanged(plugin_id, entrypoint_id, alias)) + PluginTableMsgOut::AliasChanged(plugin_id, entrypoint_id, alias) } } } diff --git a/rust/client/src/ui/windows/mod.rs b/rust/client/src/ui/windows/mod.rs index 6821ec5..bcae969 100644 --- a/rust/client/src/ui/windows/mod.rs +++ b/rust/client/src/ui/windows/mod.rs @@ -16,7 +16,7 @@ pub mod hud; #[cfg(target_os = "linux")] pub mod x11_focus; -pub struct WindowState { +pub struct MainWindowState { pub main_window_id: Option, focused: bool, #[cfg(target_os = "linux")] @@ -31,14 +31,14 @@ pub struct WindowState { open_position: Position, } -impl WindowState { +impl MainWindowState { pub fn new( window_position_file: Option, close_on_unfocus: bool, window_position_mode: WindowPositionMode, #[cfg(target_os = "linux")] wayland: bool, #[cfg(target_os = "linux")] layer_shell: bool, - ) -> WindowState { + ) -> MainWindowState { let open_position = window_position_file .as_ref() .map(|window_position_file| fs::read_to_string(window_position_file).ok()) @@ -73,7 +73,7 @@ impl WindowState { } } -impl WindowState { +impl MainWindowState { pub fn handle_action(&mut self, action: WindowActionMsg) -> Task { match action { WindowActionMsg::SetWindowPositionMode { mode } => { diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs index 7bdf57d..12fd388 100644 --- a/rust/common/src/lib.rs +++ b/rust/common/src/lib.rs @@ -1,25 +1,5 @@ -use serde::Deserialize; -use serde::Serialize; - pub mod cli; pub mod detached_process; pub mod dirs; pub mod model; pub mod rpc; - -pub const SETTINGS_ENV: &'static str = "__GAUNTLET_INTERNAL_SETTINGS__"; - -#[derive(Debug, Deserialize, Serialize)] -#[serde(tag = "type")] -pub enum SettingsEnvData { - OpenPluginPreferences { plugin_id: String }, - OpenEntrypointPreferences { plugin_id: String, entrypoint_id: String }, -} - -pub fn settings_env_data_to_string(data: SettingsEnvData) -> String { - serde_json::to_string(&data).expect("unable to serialize settings env data") -} - -pub fn settings_env_data_from_string(data: String) -> SettingsEnvData { - serde_json::from_str(&data).expect("unable to serialize settings env data") -} diff --git a/rust/common/src/rpc/backend_api.rs b/rust/common/src/rpc/backend_api.rs index 788fe2e..1b9e67d 100644 --- a/rust/common/src/rpc/backend_api.rs +++ b/rust/common/src/rpc/backend_api.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::sync::Arc; use gauntlet_utils::channel::RequestResult; @@ -7,15 +6,9 @@ use tokio::sync::Mutex; use tonic::Request; use tonic::transport::Channel; -use crate::model::DownloadStatus; use crate::model::EntrypointId; use crate::model::LocalSaveData; -use crate::model::PhysicalShortcut; use crate::model::PluginId; -use crate::model::PluginPreferenceUserData; -use crate::model::SettingsPlugin; -use crate::model::SettingsTheme; -use crate::model::WindowPositionMode; use crate::rpc::grpc::RpcBincode; use crate::rpc::grpc::RpcSaveLocalPluginRequest; use crate::rpc::grpc::rpc_backend_client::RpcBackendClient; @@ -42,69 +35,6 @@ pub trait BackendForToolsApi { async fn save_local_plugin(&self, path: String) -> RequestResult; } -#[boundary_gen(bincode, grpc)] -#[tonic::async_trait] -pub trait BackendForSettingsApi { - async fn wayland_global_shortcuts_enabled(&self) -> RequestResult; - - async fn plugins(&self) -> RequestResult>; - - async fn set_plugin_state(&self, plugin_id: PluginId, enabled: bool) -> RequestResult<()>; - - async fn set_entrypoint_state( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - enabled: bool, - ) -> RequestResult<()>; - - async fn set_global_shortcut(&self, shortcut: Option) -> RequestResult>; - - async fn get_global_shortcut(&self) -> RequestResult<(Option, Option)>; - - async fn set_global_entrypoint_shortcut( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - shortcut: Option, - ) -> RequestResult<()>; - - async fn get_global_entrypoint_shortcuts( - &self, - ) -> RequestResult)>>; - - async fn set_entrypoint_search_alias( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - alias: Option, - ) -> RequestResult<()>; - - async fn get_entrypoint_search_aliases(&self) -> RequestResult>; - - async fn set_theme(&self, theme: SettingsTheme) -> RequestResult<()>; - - async fn get_theme(&self) -> RequestResult; - - async fn set_window_position_mode(&self, mode: WindowPositionMode) -> RequestResult<()>; - - async fn get_window_position_mode(&self) -> RequestResult; - - async fn set_preference_value( - &self, - plugin_id: PluginId, - entrypoint_id: Option, - preference_id: String, - preference_value: PluginPreferenceUserData, - ) -> RequestResult<()>; - - async fn download_plugin(&self, plugin_id: PluginId) -> RequestResult<()>; - - async fn download_status(&self) -> RequestResult>; - - async fn remove_plugin(&self, plugin_id: PluginId) -> RequestResult<()>; -} - #[derive(Debug, Clone)] pub struct GrpcBackendApi { client: Arc>>, @@ -117,20 +47,6 @@ impl GrpcBackendApi { }) } - pub async fn backend_for_settings_api(&self, bytes: Vec) -> RequestResult> { - let request = RpcBincode { data: bytes }; - - let mut client = self.client.lock().await; - - let response = client - .backend_for_settings_api(Request::new(request)) - .await? - .into_inner() - .data; - - Ok(response) - } - pub async fn backend_for_cli_api(&self, bytes: Vec) -> RequestResult> { let request = RpcBincode { data: bytes }; diff --git a/rust/common/src/rpc/backend_server.rs b/rust/common/src/rpc/backend_server.rs index 36e702c..4f8ea1c 100644 --- a/rust/common/src/rpc/backend_server.rs +++ b/rust/common/src/rpc/backend_server.rs @@ -8,10 +8,8 @@ use tonic::Status; use tonic::transport::Server; use crate::rpc::backend_api::BackendForCliApi; -use crate::rpc::backend_api::BackendForSettingsApi; use crate::rpc::backend_api::BackendForToolsApi; use crate::rpc::backend_api::handle_grpc_request_backend_for_cli_api; -use crate::rpc::backend_api::handle_grpc_request_backend_for_settings_api; use crate::rpc::grpc::RpcBincode; use crate::rpc::grpc::RpcSaveLocalPluginRequest; use crate::rpc::grpc::RpcSaveLocalPluginResponse; @@ -33,12 +31,11 @@ pub async fn wait_for_backend_server() { pub async fn start_backend_server( cli: Box, tools: Box, - settings: Box, ) { let addr = "127.0.0.1:42320".parse().unwrap(); Server::builder() - .add_service(RpcBackendServer::new(RpcBackendServerImpl::new(cli, tools, settings))) + .add_service(RpcBackendServer::new(RpcBackendServerImpl::new(cli, tools))) .serve(addr) .await .expect("unable to start backend server"); @@ -47,16 +44,11 @@ pub async fn start_backend_server( struct RpcBackendServerImpl { cli: Box, tools: Box, - settings: Box, } impl RpcBackendServerImpl { - pub fn new( - cli: Box, - tools: Box, - settings: Box, - ) -> Self { - Self { cli, settings, tools } + pub fn new(cli: Box, tools: Box) -> Self { + Self { cli, tools } } } @@ -70,14 +62,6 @@ impl RpcBackend for RpcBackendServerImpl { Ok(Response::new(RpcBincode { data: encoded })) } - async fn backend_for_settings_api(&self, request: Request) -> Result, Status> { - let data = request.into_inner().data; - - let encoded = handle_grpc_request_backend_for_settings_api(self.settings.as_ref(), data).await?; - - Ok(Response::new(RpcBincode { data: encoded })) - } - async fn save_local_plugin( &self, request: Request, diff --git a/rust/common/src/rpc/frontend_api.rs b/rust/common/src/rpc/frontend_api.rs index 908b242..2e93435 100644 --- a/rust/common/src/rpc/frontend_api.rs +++ b/rust/common/src/rpc/frontend_api.rs @@ -32,6 +32,8 @@ pub trait FrontendApi { async fn hide_window(&self) -> RequestResult<()>; + async fn show_settings(&self) -> RequestResult<()>; + async fn show_preference_required_view( &self, plugin_id: PluginId, diff --git a/rust/common/src/rpc/server_grpc_api.rs b/rust/common/src/rpc/server_grpc_api.rs index df1941b..6271848 100644 --- a/rust/common/src/rpc/server_grpc_api.rs +++ b/rust/common/src/rpc/server_grpc_api.rs @@ -1,17 +1,9 @@ -use std::collections::HashMap; - use gauntlet_utils::channel::RequestResult; use gauntlet_utils_macros::boundary_gen; -use crate::model::DownloadStatus; use crate::model::EntrypointId; use crate::model::LocalSaveData; -use crate::model::PhysicalShortcut; use crate::model::PluginId; -use crate::model::PluginPreferenceUserData; -use crate::model::SettingsPlugin; -use crate::model::SettingsTheme; -use crate::model::WindowPositionMode; #[allow(async_fn_in_trait)] #[boundary_gen(in_process)] @@ -28,63 +20,4 @@ pub trait ServerGrpcApi { ) -> RequestResult<()>; async fn save_local_plugin(&self, path: String) -> RequestResult; - - async fn plugins(&self) -> RequestResult>; - - async fn set_plugin_state(&self, plugin_id: PluginId, enabled: bool) -> RequestResult<()>; - - async fn set_entrypoint_state( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - enabled: bool, - ) -> RequestResult<()>; - - async fn set_global_shortcut(&self, shortcut: Option) -> RequestResult>; - - async fn get_global_shortcut(&self) -> RequestResult)>>; - - async fn set_global_entrypoint_shortcut( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - shortcut: Option, - ) -> RequestResult<()>; - - async fn get_global_entrypoint_shortcuts( - &self, - ) -> RequestResult)>>; - - async fn set_entrypoint_search_alias( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - alias: Option, - ) -> RequestResult<()>; - - async fn get_entrypoint_search_aliases(&self) -> RequestResult>; - - async fn set_theme(&self, theme: SettingsTheme) -> RequestResult<()>; - - async fn get_theme(&self) -> RequestResult; - - async fn set_window_position_mode(&self, mode: WindowPositionMode) -> RequestResult<()>; - - async fn get_window_position_mode(&self) -> RequestResult; - - async fn set_preference_value( - &self, - plugin_id: PluginId, - entrypoint_id: Option, - preference_id: String, - preference_value: PluginPreferenceUserData, - ) -> RequestResult<()>; - - async fn download_plugin(&self, plugin_id: PluginId) -> RequestResult<()>; - - async fn download_status(&self) -> RequestResult>; - - async fn remove_plugin(&self, plugin_id: PluginId) -> RequestResult<()>; - - async fn wayland_global_shortcuts_enabled(&self) -> RequestResult; } diff --git a/rust/common_plugin_runtime/src/api.rs b/rust/common_plugin_runtime/src/api.rs index 33376c0..42268f9 100644 --- a/rust/common_plugin_runtime/src/api.rs +++ b/rust/common_plugin_runtime/src/api.rs @@ -35,6 +35,7 @@ pub trait BackendForPluginRuntimeApi { async fn ui_update_loading_bar(&self, entrypoint_id: EntrypointId, show: bool) -> RequestResult<()>; async fn ui_show_hud(&self, display: String) -> RequestResult<()>; async fn ui_hide_window(&self) -> RequestResult<()>; + async fn ui_show_settings(&self) -> RequestResult<()>; async fn ui_get_action_id_for_shortcut( &self, entrypoint_id: EntrypointId, diff --git a/rust/management_client/Cargo.toml b/rust/management_client/Cargo.toml deleted file mode 100644 index 15dfb9c..0000000 --- a/rust/management_client/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "gauntlet-management-client" -edition.workspace = true - -[dependencies] -# workspaces -gauntlet-common.workspace = true -gauntlet-common-ui.workspace = true -gauntlet-utils.workspace = true - -# shared -anyhow.workspace = true -iced.workspace = true -iced_fonts.workspace = true -tracing.workspace = true -tracing-subscriber.workspace = true -itertools.workspace = true diff --git a/rust/management_client/src/lib.rs b/rust/management_client/src/lib.rs deleted file mode 100644 index 7746327..0000000 --- a/rust/management_client/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod components; -mod theme; -mod ui; -mod views; - -pub fn start_management_client() { - ui::run(); -} diff --git a/rust/management_client/src/main.rs b/rust/management_client/src/main.rs deleted file mode 100644 index ec3b659..0000000 --- a/rust/management_client/src/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - tracing_subscriber::fmt::init(); - - gauntlet_management_client::start_management_client(); -} diff --git a/rust/plugin_runtime/src/plugins/settings.rs b/rust/plugin_runtime/src/plugins/settings.rs index 1a89536..ad86cdf 100644 --- a/rust/plugin_runtime/src/plugins/settings.rs +++ b/rust/plugin_runtime/src/plugins/settings.rs @@ -1,17 +1,22 @@ -use anyhow::anyhow; +use std::cell::RefCell; +use std::rc::Rc; + +use deno_core::OpState; use deno_core::op2; -use gauntlet_common::detached_process::CommandExt; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use crate::deno::GauntletJsError; -#[op2(fast)] -pub fn open_settings() -> Result<(), GauntletJsError> { - let current_exe = std::env::current_exe().map_err(|err| anyhow!(err))?; +#[op2(async)] +pub async fn open_settings(state: Rc>) -> Result<(), GauntletJsError> { + let api = { + let state = state.borrow(); - std::process::Command::new(current_exe) - .args(["settings"]) - .spawn_detached() - .map_err(|err| anyhow!(err))?; + let api = state.borrow::().clone(); - Ok(()) + api + }; + + api.ui_show_settings().await.map_err(Into::into) } diff --git a/rust/server/src/plugins/data_db_repository.rs b/rust/server/src/plugins/data_db_repository.rs index fa4f712..f571ecf 100644 --- a/rust/server/src/plugins/data_db_repository.rs +++ b/rust/server/src/plugins/data_db_repository.rs @@ -412,7 +412,6 @@ impl DataDbRepository { } pub fn list_plugins_and_entrypoints(&self) -> anyhow::Result)>> { - // language=SQLite let plugins = self.list_plugins()?; let result = plugins diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 0fef45a..1eab75a 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -961,6 +961,12 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { Ok(()) } + async fn ui_show_settings(&self) -> RequestResult<()> { + self.frontend_api.show_settings().await?; + + Ok(()) + } + async fn ui_get_action_id_for_shortcut( &self, entrypoint_id: EntrypointId, diff --git a/rust/server/src/plugins/loader.rs b/rust/server/src/plugins/loader.rs index c5366da..845cac7 100644 --- a/rust/server/src/plugins/loader.rs +++ b/rust/server/src/plugins/loader.rs @@ -56,7 +56,7 @@ impl PluginLoader { self.download_status_holder.download_status() } - pub fn download_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { + pub fn download_plugin(&self, plugin_id: PluginId) { let download_status_guard = self.download_status_holder.download_started(plugin_id.clone()); let data_db_repository = self.db_repository.clone(); @@ -104,8 +104,6 @@ impl PluginLoader { }) }) .expect("failed to spawn thread"); - - Ok(()) } pub fn save_local_plugin(&self, path: &str) -> anyhow::Result { diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 97e73ce..d7e2d0a 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -1,9 +1,6 @@ use std::collections::HashMap; use anyhow::anyhow; -use gauntlet_common::SETTINGS_ENV; -use gauntlet_common::SettingsEnvData; -use gauntlet_common::detached_process::CommandExt; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::DownloadStatus; use gauntlet_common::model::EntrypointId; @@ -30,7 +27,6 @@ use gauntlet_common::rpc::frontend_api::FrontendApi; use gauntlet_common::rpc::frontend_api::FrontendApiProxy; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; -use gauntlet_common::settings_env_data_to_string; use gauntlet_common_plugin_runtime::model::JsPluginCode; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsExec; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsFileSystem; @@ -174,7 +170,7 @@ impl ApplicationManager { }) } - pub fn download_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { + pub fn download_plugin(&self, plugin_id: PluginId) { self.plugin_downloader.download_plugin(plugin_id) } @@ -571,7 +567,7 @@ impl ApplicationManager { .set_global_entrypoint_shortcut(global_hotkey_manager, plugin_id, entrypoint_id, shortcut) } - pub fn get_global_entrypoint_shortcut( + pub fn get_global_entrypoint_shortcuts( &self, ) -> anyhow::Result)>> { self.settings.global_entrypoint_shortcuts() @@ -812,36 +808,6 @@ impl ApplicationManager { .expect("failed to toggle window"); } - pub fn open_settings_window(&self) { - let current_exe = std::env::current_exe().expect("unable to get current_exe"); - - std::process::Command::new(current_exe) - .args(["settings"]) - .spawn_detached() - .expect("failed to execute settings process"); - } - - pub fn open_settings_window_preferences(&self, plugin_id: PluginId, entrypoint_id: Option) { - let data = if let Some(entrypoint_id) = entrypoint_id { - SettingsEnvData::OpenEntrypointPreferences { - plugin_id: plugin_id.to_string(), - entrypoint_id: entrypoint_id.to_string(), - } - } else { - SettingsEnvData::OpenPluginPreferences { - plugin_id: plugin_id.to_string(), - } - }; - - let current_exe = std::env::current_exe().expect("unable to get current_exe"); - - std::process::Command::new(current_exe) - .args(["settings"]) - .env(SETTINGS_ENV, settings_env_data_to_string(data)) - .spawn_detached() - .expect("failed to execute settings process"); // this can fail in dev if binary was replaced by more recent compilation - } - fn reload_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { tracing::info!(target = "plugin", "Reloading plugin with id: {:?}", plugin_id); diff --git a/rust/server/src/rpc.rs b/rust/server/src/rpc.rs index 9b68e61..5d5ec35 100644 --- a/rust/server/src/rpc.rs +++ b/rust/server/src/rpc.rs @@ -1,16 +1,7 @@ -use std::collections::HashMap; - -use gauntlet_common::model::DownloadStatus; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::LocalSaveData; -use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; -use gauntlet_common::model::PluginPreferenceUserData; -use gauntlet_common::model::SettingsPlugin; -use gauntlet_common::model::SettingsTheme; -use gauntlet_common::model::WindowPositionMode; use gauntlet_common::rpc::backend_api::BackendForCliApi; -use gauntlet_common::rpc::backend_api::BackendForSettingsApi; use gauntlet_common::rpc::backend_api::BackendForToolsApi; use gauntlet_common::rpc::backend_server::start_backend_server; use gauntlet_common::rpc::server_grpc_api::ServerGrpcApi; @@ -33,7 +24,6 @@ pub async fn run_grpc_server(grpc_api: ServerGrpcApiProxy) { start_backend_server( Box::new(BackendServerImpl::new(grpc_api.clone())), Box::new(BackendServerImpl::new(grpc_api.clone())), - Box::new(BackendServerImpl::new(grpc_api.clone())), ) .await } @@ -77,197 +67,3 @@ impl BackendForToolsApi for BackendServerImpl { Ok(result) } } - -#[tonic::async_trait] -impl BackendForSettingsApi for BackendServerImpl { - async fn wayland_global_shortcuts_enabled(&self) -> RequestResult { - self.proxy.wayland_global_shortcuts_enabled().await - } - - async fn plugins(&self) -> RequestResult> { - self.proxy.plugins().await - } - - async fn set_plugin_state(&self, plugin_id: PluginId, enabled: bool) -> RequestResult<()> { - let result = self.proxy.set_plugin_state(plugin_id, enabled).await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_plugin_state' request {:?}", - err - ) - } - - Ok(()) - } - - async fn set_entrypoint_state( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - enabled: bool, - ) -> RequestResult<()> { - let result = self.proxy.set_entrypoint_state(plugin_id, entrypoint_id, enabled).await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_entrypoint_state' request {:?}", - err - ) - } - - Ok(()) - } - - async fn set_global_shortcut(&self, shortcut: Option) -> RequestResult> { - let result = self.proxy.set_global_shortcut(shortcut).await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_global_shortcut' request {:?}", - err - ) - } - - result - } - - async fn get_global_shortcut(&self) -> RequestResult<(Option, Option)> { - let result = self - .proxy - .get_global_shortcut() - .await? - .map(|(shortcut, error)| (Some(shortcut), error)) - .unwrap_or((None, None)); - - Ok(result) - } - - async fn set_global_entrypoint_shortcut( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - shortcut: Option, - ) -> RequestResult<()> { - let result = self - .proxy - .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) - .await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_global_entrypoint_shortcut' request {:?}", - err - ) - } - - result - } - - async fn get_global_entrypoint_shortcuts( - &self, - ) -> RequestResult)>> { - self.proxy.get_global_entrypoint_shortcuts().await - } - - async fn set_entrypoint_search_alias( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - alias: Option, - ) -> RequestResult<()> { - let result = self - .proxy - .set_entrypoint_search_alias(plugin_id, entrypoint_id, alias) - .await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_entrypoint_search_alias' request {:?}", - err - ) - } - - result - } - - async fn get_entrypoint_search_aliases(&self) -> RequestResult> { - self.proxy.get_entrypoint_search_aliases().await - } - - async fn set_theme(&self, theme: SettingsTheme) -> RequestResult<()> { - self.proxy.set_theme(theme).await - } - - async fn get_theme(&self) -> RequestResult { - self.proxy.get_theme().await - } - - async fn set_window_position_mode(&self, mode: WindowPositionMode) -> RequestResult<()> { - self.proxy.set_window_position_mode(mode).await - } - - async fn get_window_position_mode(&self) -> RequestResult { - self.proxy.get_window_position_mode().await - } - - async fn set_preference_value( - &self, - plugin_id: PluginId, - entrypoint_id: Option, - preference_id: String, - preference_value: PluginPreferenceUserData, - ) -> RequestResult<()> { - let result = self - .proxy - .set_preference_value(plugin_id, entrypoint_id, preference_id, preference_value) - .await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_preference_value' request {:?}", - err - ) - } - - Ok(()) - } - - async fn download_plugin(&self, plugin_id: PluginId) -> RequestResult<()> { - let result = self.proxy.download_plugin(plugin_id).await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'download_plugin' request {:?}", - err - ) - } - - Ok(()) - } - - async fn download_status(&self) -> RequestResult> { - self.proxy.download_status().await - } - - async fn remove_plugin(&self, plugin_id: PluginId) -> RequestResult<()> { - let result = self.proxy.remove_plugin(plugin_id).await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'remove_plugin' request {:?}", - err - ) - } - - Ok(()) - } -} diff --git a/schema/backend.proto b/schema/backend.proto index 9643b3d..f908d28 100644 --- a/schema/backend.proto +++ b/schema/backend.proto @@ -4,9 +4,6 @@ service RpcBackend { // cli rpc BackendForCliApi(RpcBincode) returns (RpcBincode); - // settings - rpc BackendForSettingsApi(RpcBincode) returns (RpcBincode); - // dev tools, screenshot gen rpc SaveLocalPlugin (RpcSaveLocalPluginRequest) returns (RpcSaveLocalPluginResponse); } From 3245dcee965ba462161318decefe6fc2f8a14186 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 27 Jul 2025 17:16:00 +0200 Subject: [PATCH 68/91] Focus setting window after it as created --- rust/client/src/ui/mod.rs | 7 +++++++ rust/client/src/ui/settings/ui.rs | 16 ++++++++++++---- rust/client/src/ui/sys_tray.rs | 7 ++++++- rust/server/src/plugins/mod.rs | 5 +++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 2c8e063..6e0525e 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -766,6 +766,13 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { AppMsg::IcedEvent(window_id, Event::Window(window::Event::Moved(point))) => { state.main_window_state.handle_move_event(window_id, point) } + AppMsg::IcedEvent(window_id, Event::Window(window::Event::Opened { .. })) => { + if state.settings_window_state.settings_window_id == Some(window_id) { + Task::done(AppMsg::Settings(SettingsMsg::WindowCreated)) + } else { + Task::none() + } + } AppMsg::IcedEvent(window_id, Event::Window(window::Event::Closed)) => { if state.settings_window_state.settings_window_id == Some(window_id) { Task::done(AppMsg::Settings(SettingsMsg::WindowDestroyed)) diff --git a/rust/client/src/ui/settings/ui.rs b/rust/client/src/ui/settings/ui.rs index 18bb420..a49cdc8 100644 --- a/rust/client/src/ui/settings/ui.rs +++ b/rust/client/src/ui/settings/ui.rs @@ -94,7 +94,8 @@ pub enum SettingsParams { pub enum SettingsMsg { Refresh, OpenSettings(SettingsParams), - WindowCreated(window::Id), + WindowToBeCreated(window::Id), + WindowCreated, WindowDestroyed, General(SettingsGeneralMsgIn), Plugin(SettingsPluginMsgIn), @@ -269,11 +270,18 @@ pub fn update_settings( run(state).unwrap_or_else(|err| Task::done(SettingsMsg::HandleBackendError(err.into()))) } - SettingsMsg::WindowCreated(window_id) => { + SettingsMsg::WindowToBeCreated(window_id) => { state.settings_window_id = Some(window_id); Task::none() } + SettingsMsg::WindowCreated => { + if let Some(window_id) = state.settings_window_id { + window::gain_focus(window_id) + } else { + Task::none() + } + } SettingsMsg::WindowDestroyed => { state.settings_window_id = None; @@ -301,9 +309,9 @@ pub fn update_settings( ..Default::default() }; let (_, open) = window::open(settings); - open.map(SettingsMsg::WindowCreated) + open.map(SettingsMsg::WindowToBeCreated) } - Some(window_id) => window::gain_focus(window_id), + Some(_) => Task::none(), }; Task::batch([ diff --git a/rust/client/src/ui/sys_tray.rs b/rust/client/src/ui/sys_tray.rs index d762de7..3ffdb8b 100644 --- a/rust/client/src/ui/sys_tray.rs +++ b/rust/client/src/ui/sys_tray.rs @@ -28,7 +28,12 @@ pub fn create_tray(application_manager: Arc) -> tray_icon::T }); } "GAUNTLET_OPEN_SETTING_WINDOW" => { - application_manager.open_settings_window(); + handle.spawn({ + let application_manager = application_manager.clone(); + async move { + application_manager.open_settings_window().await; + } + }); } _ => {} } diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index d7e2d0a..cb26d68 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -808,6 +808,11 @@ impl ApplicationManager { .expect("failed to toggle window"); } + pub async fn open_settings_window(&self) { + self.frontend_api.show_settings().await + .expect("failed to toggle window"); + } + fn reload_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { tracing::info!(target = "plugin", "Reloading plugin with id: {:?}", plugin_id); From dea6aee03adf01311a11e2fb9bbb21c4e7ce215c Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 27 Jul 2025 17:34:08 +0200 Subject: [PATCH 69/91] Fix formatting --- rust/server/src/plugins/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index cb26d68..eb67541 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -809,7 +809,9 @@ impl ApplicationManager { } pub async fn open_settings_window(&self) { - self.frontend_api.show_settings().await + self.frontend_api + .show_settings() + .await .expect("failed to toggle window"); } From 35afc3b0cd921c74e3efbb38b1e1d7468ddade02 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 27 Jul 2025 18:23:07 +0200 Subject: [PATCH 70/91] Update winit fork, viewport handling fix to layer shell --- Cargo.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a63c9c9..c4a2323 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5746,7 +5746,7 @@ dependencies = [ [[package]] name = "iced" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "iced_core", "iced_debug", @@ -5762,7 +5762,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "bitflags 2.9.1", "bytes", @@ -5780,7 +5780,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "iced_core", "iced_futures", @@ -5811,7 +5811,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "futures", "iced_core", @@ -5825,7 +5825,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "bitflags 2.9.1", "bytemuck", @@ -5845,7 +5845,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "iced_graphics", "iced_runtime", @@ -5854,7 +5854,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -5866,7 +5866,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "bytes", "iced_core", @@ -5879,7 +5879,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "bytemuck", "cosmic-text", @@ -5896,7 +5896,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "bitflags 2.9.1", "bytemuck", @@ -5916,7 +5916,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "iced_renderer", "iced_runtime", @@ -5931,7 +5931,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#9051ecbdec6cc211275e905e8740d4e35d0ed917" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" dependencies = [ "iced_debug", "iced_program", @@ -13276,7 +13276,7 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winit" version = "0.30.99" -source = "git+https://github.com/project-gauntlet/winit.git?rev=838583241fcd5bd855bd140a4df6fce3266a11a6#838583241fcd5bd855bd140a4df6fce3266a11a6" +source = "git+https://github.com/project-gauntlet/winit.git?rev=76548141d9975bfd84c2031f27cda19224fa6bcf#76548141d9975bfd84c2031f27cda19224fa6bcf" dependencies = [ "ahash 0.8.12", "android-activity", From f82841aad4ffde781edca7b3a0b6fdc2a5f94dd8 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Wed, 30 Jul 2025 21:11:23 +0200 Subject: [PATCH 71/91] Refactor react view container --- rust/client/src/ui/client_context.rs | 171 +++----- rust/client/src/ui/mod.rs | 383 +++++++++--------- rust/client/src/ui/server.rs | 8 +- rust/client/src/ui/state/mod.rs | 104 ++++- ...{widget_container.rs => view_container.rs} | 64 ++- rust/client/src/ui/widget/data.rs | 7 +- rust/client/src/ui/widget/data_mut.rs | 4 +- rust/client/src/ui/widget/events.rs | 8 +- rust/client/src/ui/widget/root.rs | 15 +- rust/plugin_runtime/src/ui.rs | 5 +- rust/server/src/plugins/js.rs | 4 +- rust/server/src/plugins/mod.rs | 2 +- 12 files changed, 370 insertions(+), 405 deletions(-) rename rust/client/src/ui/{widget_container.rs => view_container.rs} (77%) diff --git a/rust/client/src/ui/client_context.rs b/rust/client/src/ui/client_context.rs index d5cfa67..f30509a 100644 --- a/rust/client/src/ui/client_context.rs +++ b/rust/client/src/ui/client_context.rs @@ -7,41 +7,37 @@ use gauntlet_common::model::PluginId; use gauntlet_common::model::RootWidget; use gauntlet_common::model::UiRenderLocation; use gauntlet_common::model::UiWidgetId; -use iced::Task; use crate::model::UiViewEvent; use crate::ui::AppMsg; +use crate::ui::view_container::PluginViewContainer; use crate::ui::widget::action_panel::ActionPanel; use crate::ui::widget::events::ComponentWidgetEvent; -use crate::ui::widget_container::PluginWidgetContainer; pub struct ClientContext { - inline_views: Vec<(PluginId, PluginWidgetContainer)>, // Vec to have stable ordering + views: Vec<(PluginId, PluginViewContainer)>, // Vec to have stable ordering inline_view_shortcuts: HashMap>, - view: PluginWidgetContainer, } impl ClientContext { pub fn new() -> Self { Self { - inline_views: vec![], + views: vec![], inline_view_shortcuts: HashMap::new(), - view: PluginWidgetContainer::new(), } } - pub fn get_all_inline_view_containers(&self) -> &Vec<(PluginId, PluginWidgetContainer)> { - &self.inline_views - } - - pub fn get_first_inline_view_container(&self) -> Option<&PluginWidgetContainer> { - self.inline_views.first().map(|(_, container)| container) + pub fn get_first_inline_view_container(&self) -> Option<&PluginViewContainer> { + self.get_all_inline_view_containers() + .iter() + .next() + .map(|(_, container)| container) } pub fn get_first_inline_view_action_panel(&self) -> Option { self.get_first_inline_view_container() .map(|container| { - match self.inline_view_shortcuts.get(&container.get_plugin_id()) { + match self.inline_view_shortcuts.get(&container.plugin_id()) { None => container.get_action_panel(&HashMap::new()), Some(shortcuts) => container.get_action_panel(shortcuts), } @@ -49,41 +45,44 @@ impl ClientContext { .flatten() } - #[allow(unused)] - pub fn get_inline_view_container(&self, plugin_id: &PluginId) -> &PluginWidgetContainer { - self.inline_views + pub fn get_all_inline_view_containers(&self) -> Vec<&(PluginId, PluginViewContainer)> { + self.views .iter() - .find(|(id, _)| id == plugin_id) - .map(|(_, container)| container) - .expect("there should always be container for plugin at this point") + .filter(|(_, container)| matches!(container.render_location(), UiRenderLocation::InlineView)) + .collect() } - pub fn get_mut_inline_view_container(&mut self, plugin_id: &PluginId) -> &mut PluginWidgetContainer { - if let Some(index) = self.inline_views.iter().position(|(id, _)| id == plugin_id) { - let (_, container) = &mut self.inline_views[index]; + pub fn get_mut_or_create_view_container( + &mut self, + render_location: UiRenderLocation, + plugin_id: &PluginId, + entrypoint_id: &EntrypointId, + ) -> &mut PluginViewContainer { + if let Some(index) = self.views.iter().position(|(id, _)| id == plugin_id) { + let (_, container) = &mut self.views[index]; container } else { - self.inline_views - .push((plugin_id.clone(), PluginWidgetContainer::new())); - let (_, container) = self.inline_views.last_mut().expect("getting just pushed item"); + let container = PluginViewContainer::new(render_location, plugin_id.clone(), entrypoint_id.clone()); + self.views.push((plugin_id.clone(), container)); + let (_, container) = self.views.last_mut().expect("getting just pushed item"); container } } - pub fn get_view_container(&self) -> &PluginWidgetContainer { - &self.view + pub fn get_view_container(&self, plugin_id: &PluginId) -> Option<&PluginViewContainer> { + self.views + .iter() + .find(|(id, _)| id == plugin_id) + .filter(|(_, container)| matches!(container.render_location(), UiRenderLocation::View)) + .map(|(_, container)| container) } - pub fn get_mut_view_container(&mut self) -> &mut PluginWidgetContainer { - &mut self.view - } - - pub fn get_view_plugin_id(&self) -> PluginId { - self.view.get_plugin_id() - } - - pub fn get_view_entrypoint_id(&self) -> EntrypointId { - self.view.get_entrypoint_id() + pub fn get_mut_view_container(&mut self, plugin_id: &PluginId) -> Option<&mut PluginViewContainer> { + self.views + .iter_mut() + .find(|(id, _)| id == plugin_id) + .filter(|(_, container)| matches!(container.render_location(), UiRenderLocation::View)) + .map(|(_, container)| container) } pub fn render_ui( @@ -96,100 +95,26 @@ impl ClientContext { entrypoint_id: &EntrypointId, entrypoint_name: &str, ) -> AppMsg { - match render_location { - UiRenderLocation::InlineView => { - if let Some(_) = container.content { - self.get_mut_inline_view_container(plugin_id).replace_view( - container, - data, - plugin_id, - plugin_name, - entrypoint_id, - entrypoint_name, - ) - } else { - AppMsg::ClosePluginView(plugin_id.clone()) - } - } - UiRenderLocation::View => { - self.get_mut_view_container().replace_view( - container, - data, - plugin_id, - plugin_name, - entrypoint_id, - entrypoint_name, - ) - } - } + self.get_mut_or_create_view_container(render_location, plugin_id, entrypoint_id) + .replace_view(container, data, plugin_name, entrypoint_name) + } + + pub fn handle_event(&mut self, plugin_id: &PluginId, event: ComponentWidgetEvent) -> Option { + self.get_mut_view_container(plugin_id) + .and_then(|view| view.handle_event(plugin_id.clone(), event)) } pub fn set_inline_view_shortcuts(&mut self, shortcuts: HashMap>) { self.inline_view_shortcuts = shortcuts; } - pub fn clear_all_inline_views(&mut self) { - self.inline_views.clear() + pub fn clear_all_views(&mut self) { + self.views.clear() } - pub fn clear_inline_view(&mut self, plugin_id: &PluginId) { - if let Some(index) = self.inline_views.iter().position(|(id, _)| id == plugin_id) { - self.inline_views.remove(index); + pub fn clear_view(&mut self, plugin_id: &PluginId) { + if let Some(index) = self.views.iter().position(|(id, _)| id == plugin_id) { + self.views.remove(index); } } - - pub fn handle_event( - &mut self, - render_location: UiRenderLocation, - plugin_id: &PluginId, - event: ComponentWidgetEvent, - ) -> Option { - match render_location { - UiRenderLocation::InlineView => { - self.get_mut_inline_view_container(&plugin_id) - .handle_event(plugin_id.clone(), event) - } - UiRenderLocation::View => self.get_mut_view_container().handle_event(plugin_id.clone(), event), - } - } - - pub fn append_text(&mut self, text: &str) -> Task { - self.view.append_text(text) - } - - pub fn backspace_text(&mut self) -> Task { - self.view.backspace_text() - } - - pub fn focus_search_bar(&self, widget_id: UiWidgetId) -> Task { - self.view.focus_search_bar(widget_id) - } - - pub fn toggle_action_panel(&mut self) { - self.view.toggle_action_panel() - } - - pub fn get_action_ids(&self) -> Vec { - self.view.get_action_ids() - } - - pub fn get_focused_item_id(&self) -> Option { - self.view.get_focused_item_id() - } - - pub fn focus_up(&mut self) -> Task { - self.view.focus_up() - } - - pub fn focus_down(&mut self) -> Task { - self.view.focus_down() - } - - pub fn focus_left(&mut self) -> Task { - self.view.focus_left() - } - - pub fn focus_right(&mut self) -> Task { - self.view.focus_right() - } } diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 6e0525e..d57b480 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -74,8 +74,8 @@ mod state; #[cfg(any(target_os = "macos", target_os = "windows"))] mod sys_tray; mod theme; +mod view_container; mod widget; -mod widget_container; pub mod scenario_runner; mod server; @@ -146,11 +146,11 @@ pub enum AppMsg { entrypoint_id: EntrypointId, action_index: usize, }, - ShowNewView { + OpenNewView { plugin_id: PluginId, entrypoint_id: EntrypointId, }, - ShowNewGeneratedView { + OpenNewGeneratedView { plugin_id: PluginId, entrypoint_id: EntrypointId, action_index: usize, @@ -159,11 +159,8 @@ pub enum AppMsg { plugin_id: PluginId, entrypoint_id: EntrypointId, }, - RunGeneratedEntrypoint { - plugin_id: PluginId, - entrypoint_id: EntrypointId, - action_index: usize, - }, + RunGeneratedEntrypoint(PluginId, EntrypointId, usize), + RequestPluginViewOpen(PluginId, EntrypointId), RunSearchItemAction(SearchResult, usize), RunPluginAction { render_location: UiRenderLocation, @@ -193,7 +190,6 @@ pub enum AppMsg { IcedEvent(window::Id, Event), WidgetEvent { plugin_id: PluginId, - render_location: UiRenderLocation, widget_event: ComponentWidgetEvent, }, Noop, @@ -217,9 +213,8 @@ pub enum AppMsg { }, ShowBackendError(RequestError), CloseAllReactViews, - ClosePluginView(PluginId), - OpenPluginView(PluginId, EntrypointId), - InlineViewShortcuts { + RequestReactViewClose(PluginId), + SetInlineViewShortcuts { shortcuts: HashMap>, }, OnPrimaryActionMainViewNoPanelKeyboardWithoutFocus, @@ -238,14 +233,17 @@ pub enum AppMsg { widget_id: UiWidgetId, }, OnAnyActionPluginViewNoPanelKeyboardWithFocus { + plugin_id: PluginId, widget_id: UiWidgetId, id: Option, }, OnAnyActionPluginViewAnyPanelKeyboardWithFocus { + plugin_id: PluginId, widget_id: UiWidgetId, id: Option, }, OnAnyActionPluginViewAnyPanel { + plugin_id: PluginId, widget_id: UiWidgetId, id: Option, }, @@ -265,6 +263,7 @@ pub enum AppMsg { PendingPluginViewLoadingBar, ShowPluginViewLoadingBar, FocusPluginViewSearchBar { + plugin_id: PluginId, widget_id: UiWidgetId, }, SetTheme { @@ -397,54 +396,43 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { plugin_id, entrypoint_id, } => { - match &mut state.global_state { - GlobalState::MainView { - pending_plugin_view_data, - .. - } => { - *pending_plugin_view_data = Some(PluginViewData { - top_level_view: true, - plugin_id: plugin_id.clone(), - entrypoint_id: entrypoint_id.clone(), - action_shortcuts: HashMap::new(), - }); - - Task::batch([ - Task::done(AppMsg::OpenPluginView(plugin_id, entrypoint_id)), - Task::done(AppMsg::PendingPluginViewLoadingBar), - ]) - } - GlobalState::ErrorView { .. } => Task::none(), - GlobalState::PluginView { .. } => Task::none(), - GlobalState::PendingPluginView { .. } => Task::none(), - } + Task::batch([ + GlobalState::pending_plugin(&mut state.global_state, plugin_id.clone(), entrypoint_id.clone()), + Task::done(AppMsg::RequestPluginViewOpen(plugin_id, entrypoint_id)), + Task::done(AppMsg::PendingPluginViewLoadingBar), + ]) + } + AppMsg::OpenNewView { + plugin_id, + entrypoint_id, + } => { + Task::batch([ + GlobalState::pending_plugin(&mut state.global_state, plugin_id.clone(), entrypoint_id.clone()), + Task::done(AppMsg::RequestPluginViewOpen(plugin_id, entrypoint_id)), + Task::done(AppMsg::WindowAction(WindowActionMsg::ShowWindow)), + ]) } AppMsg::OpenGeneratedView { plugin_id, entrypoint_id, action_index, } => { - match &mut state.global_state { - GlobalState::MainView { - pending_plugin_view_data, - .. - } => { - *pending_plugin_view_data = Some(PluginViewData { - top_level_view: true, - plugin_id: plugin_id.clone(), - entrypoint_id: entrypoint_id.clone(), - action_shortcuts: HashMap::new(), - }); - - Task::batch([ - state.run_generated_entrypoint(plugin_id, entrypoint_id, action_index), - Task::done(AppMsg::PendingPluginViewLoadingBar), - ]) - } - GlobalState::ErrorView { .. } => Task::none(), - GlobalState::PluginView { .. } => Task::none(), - GlobalState::PendingPluginView { .. } => Task::none(), - } + Task::batch([ + GlobalState::pending_plugin(&mut state.global_state, plugin_id.clone(), entrypoint_id.clone()), + Task::done(AppMsg::RunGeneratedEntrypoint(plugin_id, entrypoint_id, action_index)), + Task::done(AppMsg::PendingPluginViewLoadingBar), + ]) + } + AppMsg::OpenNewGeneratedView { + plugin_id, + entrypoint_id, + action_index, + } => { + Task::batch([ + GlobalState::pending_plugin(&mut state.global_state, plugin_id.clone(), entrypoint_id.clone()), + Task::done(AppMsg::RunGeneratedEntrypoint(plugin_id, entrypoint_id, action_index)), + Task::done(AppMsg::WindowAction(WindowActionMsg::ShowWindow)), + ]) } AppMsg::RunCommand { plugin_id, @@ -455,15 +443,36 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { state.run_command(plugin_id, entrypoint_id), ]) } - AppMsg::RunGeneratedEntrypoint { - plugin_id, - entrypoint_id, - action_index, - } => { - Task::batch([ - Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)), - state.run_generated_entrypoint(plugin_id, entrypoint_id, action_index), - ]) + AppMsg::RunGeneratedEntrypoint(plugin_id, entrypoint_id, action_index) => { + state.run_generated_entrypoint(plugin_id, entrypoint_id, action_index) + } + AppMsg::RequestPluginViewOpen(plugin_id, entrypoint_id) => { + let msg = state + .application_manager + .request_render_view(plugin_id, entrypoint_id) + .map(|action_shortcuts| AppMsg::OnOpenView { action_shortcuts }) + .unwrap_or_else(|err| AppMsg::ShowBackendError(err.into())); + + Task::done(msg) + } + AppMsg::RequestReactViewClose(plugin_id) => { + state.application_manager.request_view_close(plugin_id.clone()); + state.client_context.clear_view(&plugin_id); + + Task::none() + } + AppMsg::CloseAllReactViews => { + if let GlobalState::PluginView { plugin_view_data, .. } = &state.global_state { + let plugin_id = plugin_view_data.plugin_id.clone(); + state.application_manager.request_view_close(plugin_id); + } + + for (plugin_id, _) in state.client_context.get_all_inline_view_containers() { + state.application_manager.request_view_close(plugin_id.clone()); + } + state.client_context.clear_all_views(); + + Task::none() } AppMsg::RunPluginAction { render_location, @@ -480,7 +489,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::done(AppMsg::WidgetEvent { widget_event, plugin_id, - render_location, }), ]) } @@ -488,7 +496,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::done(AppMsg::WidgetEvent { widget_event, plugin_id, - render_location, }) } } @@ -519,11 +526,14 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { let action = &search_result.entrypoint_actions[action_index]; match &action.action_type { SearchResultEntrypointActionType::Command => { - Task::done(AppMsg::RunGeneratedEntrypoint { - entrypoint_id: search_result.entrypoint_id.clone(), - plugin_id: search_result.plugin_id.clone(), - action_index, - }) + Task::batch([ + Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)), + Task::done(AppMsg::RunGeneratedEntrypoint( + search_result.plugin_id.clone(), + search_result.entrypoint_id.clone(), + action_index, + )), + ]) } SearchResultEntrypointActionType::View => { Task::done(AppMsg::OpenGeneratedView { @@ -578,15 +588,25 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { render_location, top_level_view, container, - data: images, + data, } => { let has_children = container.content.is_some(); + let close_task = if let UiRenderLocation::InlineView = render_location { + if !has_children { + Task::done(AppMsg::RequestReactViewClose(plugin_id.clone())) + } else { + Task::none() + } + } else { + Task::none() + }; + Task::batch([ Task::done(state.client_context.render_ui( render_location, container, - images, + data, &plugin_id, &plugin_name, &entrypoint_id, @@ -597,6 +617,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { has_children, render_location, }), + close_task, ]) } AppMsg::HandleRenderPluginUI { @@ -637,7 +658,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { }; if let UiRenderLocation::InlineView = render_location { - Task::batch([command, state.inline_view_shortcuts()]) + Task::batch([command, state.request_inline_view_shortcuts()]) } else { command } @@ -742,9 +763,18 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } GlobalState::ErrorView { .. } => Task::none(), - GlobalState::PluginView { sub_state, .. } => { + GlobalState::PluginView { + plugin_view_data, + sub_state, + .. + } => { + let view = state.client_context.get_mut_view_container(&plugin_view_data.plugin_id); + let Some(view) = view else { + return Task::none(); + }; + match sub_state { - PluginViewState::None => state.client_context.backspace_text(), + PluginViewState::None => view.backspace_text(), PluginViewState::ActionPanel { .. } => Task::none(), } } @@ -792,8 +822,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { AppMsg::WidgetEvent { widget_event, plugin_id, - render_location, - } => state.handle_plugin_event(widget_event, plugin_id, render_location), + } => state.handle_plugin_event(widget_event, plugin_id), AppMsg::Noop => Task::none(), AppMsg::ShowPreferenceRequiredView { plugin_id, @@ -888,8 +917,16 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } GlobalState::ErrorView { .. } => {} - GlobalState::PluginView { sub_state, .. } => { - state.client_context.toggle_action_panel(); + GlobalState::PluginView { + plugin_view_data, + sub_state, + .. + } => { + let Some(view) = state.client_context.get_mut_view_container(&plugin_view_data.plugin_id) else { + return Task::none(); + }; + + view.toggle_action_panel(); match sub_state { PluginViewState::None => PluginViewState::action_panel(sub_state, keyboard), @@ -927,7 +964,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { AppMsg::OnAnyActionMainViewInlineViewPanelKeyboardWithFocus { widget_id } => { match state.client_context.get_first_inline_view_container() { Some(container) => { - let plugin_id = container.get_plugin_id(); + let plugin_id = container.plugin_id(); Task::batch([ Task::done(AppMsg::ToggleActionPanel { keyboard: true }), @@ -942,15 +979,27 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { None => Task::none(), } } - AppMsg::OnAnyActionPluginViewNoPanelKeyboardWithFocus { widget_id, id } => { - Task::done(AppMsg::OnAnyActionPluginViewAnyPanel { widget_id, id }) + AppMsg::OnAnyActionPluginViewNoPanelKeyboardWithFocus { + widget_id, + plugin_id, + id, + } => { + Task::done(AppMsg::OnAnyActionPluginViewAnyPanel { + widget_id, + plugin_id, + id, + }) } - AppMsg::OnAnyActionPluginViewAnyPanelKeyboardWithFocus { widget_id, id } => { + AppMsg::OnAnyActionPluginViewAnyPanelKeyboardWithFocus { + widget_id, + id, + plugin_id, + } => { Task::batch([ Task::done(AppMsg::ToggleActionPanel { keyboard: true }), Task::done(AppMsg::RunPluginAction { render_location: UiRenderLocation::View, - plugin_id: state.client_context.get_view_plugin_id(), + plugin_id, widget_id, id, }), @@ -975,7 +1024,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } AppMsg::OnAnyActionMainViewNoPanelKeyboardAtIndex { index } => { if let Some(container) = state.client_context.get_first_inline_view_container() { - let plugin_id = container.get_plugin_id(); + let plugin_id = container.plugin_id(); let action_ids = container.get_action_ids(); match action_ids.get(index) { @@ -1023,35 +1072,19 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { GlobalState::PendingPluginView { .. } => Task::none(), } } - AppMsg::OnAnyActionPluginViewAnyPanel { widget_id, id } => { + AppMsg::OnAnyActionPluginViewAnyPanel { + widget_id, + id, + plugin_id, + } => { Task::done(AppMsg::RunPluginAction { render_location: UiRenderLocation::View, - plugin_id: state.client_context.get_view_plugin_id(), + plugin_id, widget_id, id, }) } - AppMsg::OpenPluginView(plugin_id, entrypoint_id) => state.open_plugin_view(plugin_id, entrypoint_id), - AppMsg::CloseAllReactViews => { - if let GlobalState::PluginView { plugin_view_data, .. } = &state.global_state { - let plugin_id = plugin_view_data.plugin_id.clone(); - state.application_manager.request_view_close(plugin_id); - } - - for (plugin_id, _) in state.client_context.get_all_inline_view_containers() { - state.application_manager.request_view_close(plugin_id.clone()); - } - state.client_context.clear_all_inline_views(); - - Task::none() - } - AppMsg::ClosePluginView(plugin_id) => { - state.application_manager.request_view_close(plugin_id.clone()); - state.client_context.clear_inline_view(&plugin_id); - - Task::none() - } - AppMsg::InlineViewShortcuts { shortcuts } => { + AppMsg::SetInlineViewShortcuts { shortcuts } => { state.client_context.set_inline_view_shortcuts(shortcuts); Task::none() @@ -1112,7 +1145,13 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } - AppMsg::FocusPluginViewSearchBar { widget_id } => state.client_context.focus_search_bar(widget_id), + AppMsg::FocusPluginViewSearchBar { plugin_id, widget_id } => { + let Some(view) = state.client_context.get_view_container(&plugin_id) else { + return Task::none(); + }; + + view.focus_search_bar(widget_id) + } AppMsg::WindowAction(action) => state.main_window_state.handle_action(action), AppMsg::SetTheme { theme } => { state.theme = GauntletComplexTheme::new(theme); @@ -1121,43 +1160,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } - AppMsg::ShowNewView { - plugin_id, - entrypoint_id, - } => { - Task::batch([ - GlobalState::pending_plugin( - &mut state.global_state, - PluginViewData { - top_level_view: true, - plugin_id: plugin_id.clone(), - entrypoint_id: entrypoint_id.clone(), - action_shortcuts: HashMap::new(), - }, - ), - Task::done(AppMsg::OpenPluginView(plugin_id, entrypoint_id)), - Task::done(AppMsg::WindowAction(WindowActionMsg::ShowWindow)), - ]) - } - AppMsg::ShowNewGeneratedView { - plugin_id, - entrypoint_id, - action_index, - } => { - Task::batch([ - GlobalState::pending_plugin( - &mut state.global_state, - PluginViewData { - top_level_view: true, - plugin_id: plugin_id.clone(), - entrypoint_id: entrypoint_id.clone(), - action_shortcuts: HashMap::new(), - }, - ), - state.run_generated_entrypoint(plugin_id, entrypoint_id, action_index), - Task::done(AppMsg::WindowAction(WindowActionMsg::ShowWindow)), - ]) - } AppMsg::HandleGlobalShortcut(event) => { match state.application_manager.handle_global_shortcut_event(event) { Ok(action) => { @@ -1502,17 +1504,16 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { }; let inline_view = match state.client_context.get_all_inline_view_containers().first() { + None => horizontal_space().into(), Some((plugin_id, container)) => { let plugin_id = plugin_id.clone(); container.render_inline_root_widget().map(move |widget_event| { AppMsg::WidgetEvent { plugin_id: plugin_id.clone(), - render_location: UiRenderLocation::InlineView, widget_event, } }) } - None => horizontal_space().into(), }; let content: Element<_> = column(vec![inline_view, list]).into(); @@ -1750,18 +1751,19 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { .. } = plugin_view_data; - let view_container = state.client_context.get_view_container(); - - let container_element = - view_container - .render_root_widget(sub_state, action_shortcuts) - .map(|widget_event| { - AppMsg::WidgetEvent { - plugin_id: plugin_id.clone(), - render_location: UiRenderLocation::View, - widget_event, - } - }); + let container_element = match state.client_context.get_view_container(plugin_id) { + None => horizontal_space().into(), + Some(view_container) => { + view_container + .render_root_widget(sub_state, action_shortcuts) + .map(|widget_event| { + AppMsg::WidgetEvent { + plugin_id: plugin_id.clone(), + widget_event, + } + }) + } + }; let element: Element<_> = container(container_element) .width(Length::Fill) @@ -1822,21 +1824,11 @@ impl AppModel { fn reset_window_state(&mut self) -> Task { self.prompt = "".to_string(); - self.client_context.clear_all_inline_views(); + self.client_context.clear_all_views(); GlobalState::initial(&mut self.global_state) } - fn open_plugin_view(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> Task { - let msg = self - .application_manager - .request_render_view(plugin_id, entrypoint_id) - .map(|action_shortcuts| AppMsg::OnOpenView { action_shortcuts }) - .unwrap_or_else(|err| AppMsg::ShowBackendError(err.into())); - - Task::done(msg) - } - fn run_command(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) -> Task { self.application_manager.run_command(plugin_id, entrypoint_id); @@ -1855,17 +1847,10 @@ impl AppModel { Task::none() } - fn handle_plugin_event( - &mut self, - widget_event: ComponentWidgetEvent, - plugin_id: PluginId, - render_location: UiRenderLocation, - ) -> Task { + fn handle_plugin_event(&mut self, widget_event: ComponentWidgetEvent, plugin_id: PluginId) -> Task { let application_manager = self.application_manager.clone(); - let event = self - .client_context - .handle_event(render_location, &plugin_id, widget_event.clone()); + let event = self.client_context.handle_event(&plugin_id, widget_event.clone()); if let Some(event) = event { match event { @@ -1921,19 +1906,14 @@ impl AppModel { fn handle_plugin_view_keyboard_event( &self, + plugin_id: PluginId, + entrypoint_id: EntrypointId, physical_key: PhysicalKey, modifier_shift: bool, modifier_control: bool, modifier_alt: bool, modifier_meta: bool, ) -> Task { - let (plugin_id, entrypoint_id) = { - ( - self.client_context.get_view_plugin_id(), - self.client_context.get_view_entrypoint_id(), - ) - }; - self.application_manager.handle_keyboard_event( plugin_id, entrypoint_id, @@ -1956,11 +1936,9 @@ impl AppModel { modifier_alt: bool, modifier_meta: bool, ) -> Task { - let (plugin_id, entrypoint_id) = { - match self.client_context.get_first_inline_view_container() { - None => return Task::none(), - Some(container) => (container.get_plugin_id(), container.get_entrypoint_id()), - } + let (plugin_id, entrypoint_id) = match self.client_context.get_first_inline_view_container() { + None => return Task::none(), + Some(container) => (container.plugin_id(), container.entrypoint_id()), }; self.application_manager.handle_keyboard_event( @@ -1987,11 +1965,11 @@ impl AppModel { Task::done(msg) } - fn inline_view_shortcuts(&self) -> Task { + fn request_inline_view_shortcuts(&self) -> Task { let result = self .application_manager .inline_view_shortcuts() - .map(|shortcuts| AppMsg::InlineViewShortcuts { shortcuts }) + .map(|shortcuts| AppMsg::SetInlineViewShortcuts { shortcuts }) .unwrap_or_else(|err| AppMsg::ShowBackendError(err.into())); Task::done(result) @@ -2144,7 +2122,18 @@ impl AppModel { } } GlobalState::ErrorView { .. } => Task::none(), - GlobalState::PluginView { sub_state, .. } => { + GlobalState::PluginView { + plugin_view_data, + sub_state, + .. + } => { + let plugin_id = plugin_view_data.plugin_id.clone(); + let entrypoint_id = plugin_view_data.entrypoint_id.clone(); + + let Some(view) = self.client_context.get_mut_view_container(&plugin_view_data.plugin_id) else { + return Task::none(); + }; + match physical_key_model(physical_key, modifiers) { Some(PhysicalShortcut { physical_key: PhysicalKey::KeyK, @@ -2162,6 +2151,8 @@ impl AppModel { }) => { if modifier_shift || modifier_control || modifier_alt || modifier_meta { self.handle_plugin_view_keyboard_event( + plugin_id, + entrypoint_id, physical_key, modifier_shift, modifier_control, @@ -2173,7 +2164,7 @@ impl AppModel { PluginViewState::None => { match text { None => Task::none(), - Some(text) => self.client_context.append_text(text.as_str()), + Some(text) => view.append_text(text.as_str()), } } PluginViewState::ActionPanel { .. } => Task::none(), diff --git a/rust/client/src/ui/server.rs b/rust/client/src/ui/server.rs index 6c3066c..571e258 100644 --- a/rust/client/src/ui/server.rs +++ b/rust/client/src/ui/server.rs @@ -125,7 +125,7 @@ async fn request_loop( render_location, top_level_view, container, - data: images, + data, } => { responder.respond(Ok(FrontendApiResponseData::ReplaceView { data: () })); @@ -137,7 +137,7 @@ async fn request_loop( render_location, top_level_view, container: Arc::new(container), - data: images, + data, } } FrontendApiRequestData::ToggleWindow {} => { @@ -216,7 +216,7 @@ async fn request_loop( } => { responder.respond(Ok(FrontendApiResponseData::OpenPluginView { data: () })); - AppMsg::ShowNewView { + AppMsg::OpenNewView { plugin_id, entrypoint_id, } @@ -228,7 +228,7 @@ async fn request_loop( } => { responder.respond(Ok(FrontendApiResponseData::OpenGeneratedPluginView { data: () })); - AppMsg::ShowNewGeneratedView { + AppMsg::OpenNewGeneratedView { plugin_id, entrypoint_id, action_index, diff --git a/rust/client/src/ui/state/mod.rs b/rust/client/src/ui/state/mod.rs index ea11bc8..93751a7 100644 --- a/rust/client/src/ui/state/mod.rs +++ b/rust/client/src/ui/state/mod.rs @@ -134,8 +134,19 @@ impl GlobalState { Task::none() } - pub fn pending_plugin(prev_global_state: &mut GlobalState, plugin_view_data: PluginViewData) -> Task { - *prev_global_state = GlobalState::new_pending_plugin(plugin_view_data); + pub fn pending_plugin( + prev_global_state: &mut GlobalState, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + ) -> Task { + let view_data = PluginViewData { + top_level_view: true, + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + action_shortcuts: HashMap::new(), + }; + + *prev_global_state = GlobalState::new_pending_plugin(view_data); Task::none() } @@ -198,15 +209,24 @@ impl Focus for GlobalState { } } } - GlobalState::PluginView { sub_state, .. } => { - let action_ids = client_context.get_action_ids(); - let focused_item_id = client_context.get_focused_item_id(); + GlobalState::PluginView { + plugin_view_data, + sub_state, + .. + } => { + let Some(view) = client_context.get_view_container(&plugin_view_data.plugin_id) else { + return Task::none(); + }; + + let action_ids = view.get_action_ids(); + let focused_item_id = view.get_focused_item_id(); match sub_state { PluginViewState::None => { if let Some(widget_id) = action_ids.get(0) { let widget_id = *widget_id; Task::done(AppMsg::OnAnyActionPluginViewNoPanelKeyboardWithFocus { + plugin_id: plugin_view_data.plugin_id.clone(), widget_id, id: focused_item_id, }) @@ -220,6 +240,7 @@ impl Focus for GlobalState { if let Some(widget_id) = focused_action_item.get(&action_ids) { let widget_id = *widget_id; Task::done(AppMsg::OnAnyActionPluginViewAnyPanelKeyboardWithFocus { + plugin_id: plugin_view_data.plugin_id.clone(), widget_id, id: focused_item_id, }) @@ -256,15 +277,24 @@ impl Focus for GlobalState { } } } - GlobalState::PluginView { sub_state, .. } => { - let action_ids = client_context.get_action_ids(); - let focused_item_id = client_context.get_focused_item_id(); + GlobalState::PluginView { + plugin_view_data, + sub_state, + .. + } => { + let Some(container) = client_context.get_view_container(&plugin_view_data.plugin_id) else { + return Task::none(); + }; + + let action_ids = container.get_action_ids(); + let focused_item_id = container.get_focused_item_id(); match sub_state { PluginViewState::None => { if let Some(widget_id) = action_ids.get(1) { let widget_id = *widget_id; Task::done(AppMsg::OnAnyActionPluginViewNoPanelKeyboardWithFocus { + plugin_id: plugin_view_data.plugin_id.clone(), widget_id, id: focused_item_id, }) @@ -314,19 +344,17 @@ impl Focus for GlobalState { if *top_level_view { if *close_window_on_esc { Task::batch([ - Task::done(AppMsg::ClosePluginView(plugin_id.clone())), + Task::done(AppMsg::RequestReactViewClose(plugin_id.clone())), Task::done(AppMsg::WindowAction(WindowActionMsg::HideWindow)), ]) } else { Task::batch([ - Task::done(AppMsg::ClosePluginView(plugin_id.clone())), + Task::done(AppMsg::RequestReactViewClose(plugin_id.clone())), GlobalState::initial(self), ]) } } else { - let plugin_id = plugin_id.clone(); - let entrypoint_id = entrypoint_id.clone(); - Task::done(AppMsg::OpenPluginView(plugin_id, entrypoint_id)) + Task::done(AppMsg::RequestPluginViewOpen(plugin_id.clone(), entrypoint_id.clone())) } } PluginViewState::ActionPanel { .. } => Task::done(AppMsg::ToggleActionPanel { keyboard: true }), @@ -370,9 +398,17 @@ impl Focus for GlobalState { } } GlobalState::ErrorView { .. } => Task::none(), - GlobalState::PluginView { sub_state, .. } => { + GlobalState::PluginView { + plugin_view_data, + sub_state, + .. + } => { + let Some(view) = client_context.get_mut_view_container(&plugin_view_data.plugin_id) else { + return Task::none(); + }; + match sub_state { - PluginViewState::None => client_context.focus_up(), + PluginViewState::None => view.focus_up(), PluginViewState::ActionPanel { focused_action_item } => { focused_action_item.focus_previous().unwrap_or_else(|| Task::none()) } @@ -428,11 +464,19 @@ impl Focus for GlobalState { } } GlobalState::ErrorView { .. } => Task::none(), - GlobalState::PluginView { sub_state, .. } => { + GlobalState::PluginView { + plugin_view_data, + sub_state, + .. + } => { + let Some(view) = client_context.get_mut_view_container(&plugin_view_data.plugin_id) else { + return Task::none(); + }; + match sub_state { - PluginViewState::None => client_context.focus_down(), + PluginViewState::None => view.focus_down(), PluginViewState::ActionPanel { focused_action_item } => { - let action_ids = client_context.get_action_ids(); + let action_ids = view.get_action_ids(); if action_ids.len() != 0 { focused_action_item @@ -449,9 +493,17 @@ impl Focus for GlobalState { } fn left(&mut self, client_context: &mut ClientContext, _focus_list: &[SearchResult]) -> Task { match self { - GlobalState::PluginView { sub_state, .. } => { + GlobalState::PluginView { + plugin_view_data, + sub_state, + .. + } => { + let Some(view) = client_context.get_mut_view_container(&plugin_view_data.plugin_id) else { + return Task::none(); + }; + match sub_state { - PluginViewState::None => client_context.focus_left(), + PluginViewState::None => view.focus_left(), PluginViewState::ActionPanel { .. } => Task::none(), } } @@ -462,9 +514,17 @@ impl Focus for GlobalState { } fn right(&mut self, client_context: &mut ClientContext, _focus_list: &[SearchResult]) -> Task { match self { - GlobalState::PluginView { sub_state, .. } => { + GlobalState::PluginView { + plugin_view_data, + sub_state, + .. + } => { + let Some(view) = client_context.get_mut_view_container(&plugin_view_data.plugin_id) else { + return Task::none(); + }; + match sub_state { - PluginViewState::None => client_context.focus_right(), + PluginViewState::None => view.focus_right(), PluginViewState::ActionPanel { .. } => Task::none(), } } diff --git a/rust/client/src/ui/widget_container.rs b/rust/client/src/ui/view_container.rs similarity index 77% rename from rust/client/src/ui/widget_container.rs rename to rust/client/src/ui/view_container.rs index 3c1f3b4..e53926f 100644 --- a/rust/client/src/ui/widget_container.rs +++ b/rust/client/src/ui/view_container.rs @@ -7,6 +7,7 @@ use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; use gauntlet_common::model::RootWidget; +use gauntlet_common::model::UiRenderLocation; use gauntlet_common::model::UiWidgetId; use iced::Task; @@ -21,55 +22,53 @@ use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::state::ComponentWidgetState; use crate::ui::widget::state::create_state; -pub struct PluginWidgetContainer { +pub struct PluginViewContainer { root_widget: Option>, state: HashMap, data: HashMap>, - plugin_id: Option, + render_location: UiRenderLocation, + plugin_id: PluginId, plugin_name: Option, - entrypoint_id: Option, + entrypoint_id: EntrypointId, entrypoint_name: Option, } -impl PluginWidgetContainer { - pub fn new() -> Self { +impl PluginViewContainer { + pub fn new(render_location: UiRenderLocation, plugin_id: PluginId, entrypoint_id: EntrypointId) -> Self { Self { root_widget: None, state: HashMap::new(), data: HashMap::new(), - plugin_id: None, + render_location, + plugin_id, plugin_name: None, - entrypoint_id: None, + entrypoint_id, entrypoint_name: None, } } - pub fn get_plugin_id(&self) -> PluginId { - self.plugin_id - .clone() - .expect("plugin id should always exist after render") + pub fn plugin_id(&self) -> PluginId { + self.plugin_id.clone() } - pub fn get_entrypoint_id(&self) -> EntrypointId { - self.entrypoint_id - .clone() - .expect("entrypoint id should always exist after render") + pub fn entrypoint_id(&self) -> EntrypointId { + self.entrypoint_id.clone() + } + + pub fn render_location(&self) -> UiRenderLocation { + self.render_location.clone() } pub fn replace_view( &mut self, container: Arc, data: HashMap>, - plugin_id: &PluginId, plugin_name: &str, - entrypoint_id: &EntrypointId, entrypoint_name: &str, ) -> AppMsg { tracing::trace!("replace_view is called. container: {:?}", container); - self.plugin_id = Some(plugin_id.clone()); self.plugin_name = Some(plugin_name.to_string()); - self.entrypoint_id = Some(entrypoint_id.clone()); self.entrypoint_name = Some(entrypoint_name.to_string()); self.data = data; @@ -98,7 +97,7 @@ impl PluginWidgetContainer { self.root_widget = Some(container); if first_open { - ComponentWidgets::new(&self.root_widget, &self.state, &self.data).first_open() + ComponentWidgets::new(&self.root_widget, &self.state, &self.data).first_open(self.plugin_id.clone()) } else { AppMsg::Noop } @@ -117,24 +116,22 @@ impl PluginWidgetContainer { ) -> Element<'a, ComponentWidgetEvent> { ComponentWidgets::new(&self.root_widget, &self.state, &self.data).render_root_widget( plugin_view_state, - self.entrypoint_name.as_ref(), + self.entrypoint_name.as_deref(), action_shortcuts, ) } pub fn render_inline_root_widget<'a>(&self) -> Element<'a, ComponentWidgetEvent> { ComponentWidgets::new(&self.root_widget, &self.state, &self.data) - .render_root_inline_widget(self.plugin_name.as_ref(), self.entrypoint_name.as_ref()) + .render_root_inline_widget(self.plugin_name.as_deref(), self.entrypoint_name.as_deref()) } pub fn append_text(&mut self, text: &str) -> Task { - let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).append_text(text) + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).append_text(text) } pub fn backspace_text(&mut self) -> Task { - let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).backspace_text() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).backspace_text() } pub fn focus_search_bar(&self, widget_id: UiWidgetId) -> Task { @@ -142,8 +139,7 @@ impl PluginWidgetContainer { } pub fn toggle_action_panel(&mut self) { - let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).toggle_action_panel() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).toggle_action_panel() } pub fn get_action_ids(&self) -> Vec { @@ -159,22 +155,18 @@ impl PluginWidgetContainer { } pub fn focus_up(&mut self) -> Task { - let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).focus_up() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).focus_up() } pub fn focus_down(&mut self) -> Task { - let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).focus_down() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).focus_down() } pub fn focus_left(&mut self) -> Task { - let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).focus_left() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).focus_left() } pub fn focus_right(&mut self) -> Task { - let plugin_id = self.get_plugin_id(); - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, plugin_id).focus_right() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).focus_right() } } diff --git a/rust/client/src/ui/widget/data.rs b/rust/client/src/ui/widget/data.rs index a0640a1..4adfa50 100644 --- a/rust/client/src/ui/widget/data.rs +++ b/rust/client/src/ui/widget/data.rs @@ -13,7 +13,6 @@ use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; use gauntlet_common::model::RootWidget; use gauntlet_common::model::RootWidgetMembers; -use gauntlet_common::model::UiRenderLocation; use gauntlet_common::model::UiWidgetId; use iced::Task; use iced::widget::text_input; @@ -247,7 +246,7 @@ impl<'b> ComponentWidgets<'b> { } impl<'b> ComponentWidgets<'b> { - pub fn first_open(&self) -> AppMsg { + pub fn first_open(&self, plugin_id: PluginId) -> AppMsg { let Some(root_widget) = &self.root_widget else { return AppMsg::Noop; }; @@ -272,7 +271,7 @@ impl<'b> ComponentWidgets<'b> { _ => return AppMsg::Noop, }; - AppMsg::FocusPluginViewSearchBar { widget_id } + AppMsg::FocusPluginViewSearchBar { plugin_id, widget_id } } pub fn list_focused_item_id(focused_item: &ScrollHandle, widget: &ListWidget) -> Option { @@ -323,7 +322,6 @@ impl<'b> ComponentWidgets<'b> { Task::done(AppMsg::WidgetEvent { plugin_id, - render_location: UiRenderLocation::View, widget_event, }) } @@ -376,7 +374,6 @@ impl<'b> ComponentWidgets<'b> { Task::done(AppMsg::WidgetEvent { plugin_id, - render_location: UiRenderLocation::View, widget_event, }) } diff --git a/rust/client/src/ui/widget/data_mut.rs b/rust/client/src/ui/widget/data_mut.rs index ad9ee38..d3dd584 100644 --- a/rust/client/src/ui/widget/data_mut.rs +++ b/rust/client/src/ui/widget/data_mut.rs @@ -31,12 +31,12 @@ impl<'b> ComponentWidgetsMut<'b> { pub fn new( root_widget: &'b mut Option>, state: &'b mut HashMap, - plugin_id: PluginId, + plugin_id: &PluginId, ) -> ComponentWidgetsMut<'b> { Self { root_widget, state, - plugin_id, + plugin_id: plugin_id.clone(), } } diff --git a/rust/client/src/ui/widget/events.rs b/rust/client/src/ui/widget/events.rs index 67642c1..390ade2 100644 --- a/rust/client/src/ui/widget/events.rs +++ b/rust/client/src/ui/widget/events.rs @@ -67,7 +67,7 @@ pub enum ComponentWidgetEvent { } impl ComponentWidgetEvent { - pub fn handle(self, _plugin_id: PluginId, state: Option<&mut ComponentWidgetState>) -> Option { + pub fn handle(self, plugin_id: PluginId, state: Option<&mut ComponentWidgetState>) -> Option { match self { ComponentWidgetEvent::LinkClick { widget_id: _, href } => Some(UiViewEvent::Open { href }), ComponentWidgetEvent::TagClick { widget_id } => Some(create_metadata_tag_item_on_click_event(widget_id)), @@ -157,7 +157,11 @@ impl ComponentWidgetEvent { } ComponentWidgetEvent::RunPrimaryAction { widget_id, id } => { Some(UiViewEvent::AppEvent { - event: AppMsg::OnAnyActionPluginViewAnyPanel { widget_id, id }, + event: AppMsg::OnAnyActionPluginViewAnyPanel { + plugin_id, + widget_id, + id, + }, }) } } diff --git a/rust/client/src/ui/widget/root.rs b/rust/client/src/ui/widget/root.rs index 55afa91..c215047 100644 --- a/rust/client/src/ui/widget/root.rs +++ b/rust/client/src/ui/widget/root.rs @@ -44,7 +44,7 @@ impl<'b> ComponentWidgets<'b> { pub fn render_root_widget<'a>( &self, plugin_view_state: &PluginViewState, - entrypoint_name: Option<&String>, + entrypoint_name: Option<&str>, action_shortcuts: &HashMap, ) -> Element<'a, ComponentWidgetEvent> { match &self.root_widget { @@ -53,8 +53,7 @@ impl<'b> ComponentWidgets<'b> { match &root.content { None => horizontal_space().into(), Some(content) => { - let entrypoint_name = - entrypoint_name.expect("entrypoint name should always exist after render"); + let entrypoint_name = entrypoint_name.unwrap_or_default(); match content { RootWidgetMembers::Detail(widget) => { @@ -96,8 +95,8 @@ impl<'b> ComponentWidgets<'b> { pub fn render_root_inline_widget<'a>( &self, - plugin_name: Option<&String>, - entrypoint_name: Option<&String>, + plugin_name: Option<&str>, + entrypoint_name: Option<&str>, ) -> Element<'a, ComponentWidgetEvent> { match &self.root_widget { None => horizontal_space().into(), @@ -107,10 +106,8 @@ impl<'b> ComponentWidgets<'b> { Some(content) => { match content { RootWidgetMembers::Inline(widget) => { - let entrypoint_name = - entrypoint_name.expect("entrypoint name should always exist after render"); - let plugin_name = - plugin_name.expect("entrypoint name should always exist after render"); + let entrypoint_name = entrypoint_name.unwrap_or_default(); + let plugin_name = plugin_name.unwrap_or_default(); self.render_inline_widget(widget, plugin_name, entrypoint_name) } diff --git a/rust/plugin_runtime/src/ui.rs b/rust/plugin_runtime/src/ui.rs index 43ff067..feeb6e3 100644 --- a/rust/plugin_runtime/src/ui.rs +++ b/rust/plugin_runtime/src/ui.rs @@ -90,8 +90,8 @@ pub fn op_react_replace_view<'a>( state: Rc>, #[serde] render_location: JsUiRenderLocation, top_level_view: bool, - #[string] entrypoint_id: &str, - #[string] entrypoint_name: &str, + #[string] entrypoint_id: String, + #[string] entrypoint_name: String, container: v8::Local, ) -> Result<(), GauntletJsError> { tracing::trace!(target = "renderer_rs", "Calling op_react_replace_view..."); @@ -100,7 +100,6 @@ pub fn op_react_replace_view<'a>( .map_err(|err| anyhow!("Unable to deserialize component tree: {:#}", err))?; let entrypoint_id = EntrypointId::from_string(entrypoint_id); - let entrypoint_name = entrypoint_name.to_string(); let (api, outer_handle) = { let state = state.borrow(); diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 1eab75a..0ddc345 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -113,7 +113,7 @@ pub enum PluginCommand { #[derive(Clone, Debug)] pub enum OnePluginCommandData { - RenderView { + OpenView { entrypoint_id: EntrypointId, }, CloseView, @@ -402,7 +402,7 @@ async fn event_loop( None } else { match data { - OnePluginCommandData::RenderView { entrypoint_id } => { + OnePluginCommandData::OpenView { entrypoint_id } => { Some(IntermediateUiEvent::OpenView { entrypoint_id }) } OnePluginCommandData::CloseView => Some(IntermediateUiEvent::CloseView), diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index eb67541..8db4f3a 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -726,7 +726,7 @@ impl ApplicationManager { ) -> anyhow::Result> { self.send_command(PluginCommand::One { id: plugin_id.clone(), - data: OnePluginCommandData::RenderView { + data: OnePluginCommandData::OpenView { entrypoint_id: entrypoint_id.clone(), }, }); From 39bf811b113448a87e02e1775cc8d1f16f3c1090 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 31 Jul 2025 20:18:56 +0200 Subject: [PATCH 72/91] Fix main window disappearing and reappearing when opening a plugin view --- rust/client/src/ui/mod.rs | 12 ++++++++++-- rust/client/src/ui/state/mod.rs | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index d57b480..8e3f75b 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -397,7 +397,11 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { entrypoint_id, } => { Task::batch([ - GlobalState::pending_plugin(&mut state.global_state, plugin_id.clone(), entrypoint_id.clone()), + GlobalState::pending_plugin_main_view( + &mut state.global_state, + plugin_id.clone(), + entrypoint_id.clone(), + ), Task::done(AppMsg::RequestPluginViewOpen(plugin_id, entrypoint_id)), Task::done(AppMsg::PendingPluginViewLoadingBar), ]) @@ -418,7 +422,11 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { action_index, } => { Task::batch([ - GlobalState::pending_plugin(&mut state.global_state, plugin_id.clone(), entrypoint_id.clone()), + GlobalState::pending_plugin_main_view( + &mut state.global_state, + plugin_id.clone(), + entrypoint_id.clone(), + ), Task::done(AppMsg::RunGeneratedEntrypoint(plugin_id, entrypoint_id, action_index)), Task::done(AppMsg::PendingPluginViewLoadingBar), ]) diff --git a/rust/client/src/ui/state/mod.rs b/rust/client/src/ui/state/mod.rs index 93751a7..3863dc4 100644 --- a/rust/client/src/ui/state/mod.rs +++ b/rust/client/src/ui/state/mod.rs @@ -150,6 +150,27 @@ impl GlobalState { Task::none() } + + pub fn pending_plugin_main_view( + prev_global_state: &mut GlobalState, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + ) -> Task { + if let GlobalState::MainView { + pending_plugin_view_data, + .. + } = prev_global_state + { + *pending_plugin_view_data = Some(PluginViewData { + top_level_view: true, + plugin_id: plugin_id.clone(), + entrypoint_id: entrypoint_id.clone(), + action_shortcuts: HashMap::new(), + }); + } + + Task::none() + } } pub trait Focus { From 0528b96fae7a75da33ebf5e430f7ae555c07c5b1 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Thu, 31 Jul 2025 21:25:57 +0200 Subject: [PATCH 73/91] Fix back navigation being treated as whole separate view open session --- js/core/src/core.tsx | 21 +++++++++++++++++++-- js/core/src/render.tsx | 6 +++++- js/react_renderer/src/renderer.ts | 4 ++++ js/typings/index.d.ts | 8 +++++++- rust/client/src/ui/mod.rs | 6 ++++++ rust/client/src/ui/state/mod.rs | 2 +- rust/common_plugin_runtime/src/model.rs | 4 ++++ rust/server/src/model.rs | 3 +++ rust/server/src/plugins/js.rs | 11 +++++++++++ rust/server/src/plugins/mod.rs | 7 +++++++ 10 files changed, 67 insertions(+), 5 deletions(-) diff --git a/js/core/src/core.tsx b/js/core/src/core.tsx index aa4b2a4..2afa162 100644 --- a/js/core/src/core.tsx +++ b/js/core/src/core.tsx @@ -1,7 +1,13 @@ import type { FC } from "react"; import { runEntrypointGenerators, runGeneratedEntrypoint, runGeneratedEntrypointAction } from "./entrypoint-generator"; import { reloadSearchIndex } from "./search-index"; -import { closeView, handleEvent, handlePluginViewKeyboardEvent, renderInlineView, renderView } from "./render"; +import { + closeView, + handleEvent, + handlePluginViewKeyboardEvent, popMainView, + renderInlineView, + renderView, +} from "./render"; import { entrypoint_preferences_required, get_entrypoint_preferences, @@ -94,6 +100,16 @@ export async function runPluginLoop() { closeView() break; } + case "PopView": { + const entrypointId = pluginEvent.entrypointId + try { + popMainView() + } catch (e) { + console.error("Error occurred when popping view", entrypointId, e) + show_plugin_error_view(entrypointId, "View") + } + break; + } case "RunCommand": { try { if (await checkRequiredPreferencesAndAsk(pluginEvent.entrypointId)) { @@ -132,7 +148,8 @@ export async function runPluginLoop() { } try { - const handler: FC<{ text: string }> = (await import(`gauntlet:entrypoint?${entrypointId}`)).default; + type InlineView = { text: string }; + const handler: FC = (await import(`gauntlet:entrypoint?${entrypointId}`)).default; renderInlineView(entrypointId, getEntrypointName(entrypointId), handler, pluginEvent.text) } catch (e) { diff --git a/js/core/src/render.tsx b/js/core/src/render.tsx index dd91ec6..22936da 100644 --- a/js/core/src/render.tsx +++ b/js/core/src/render.tsx @@ -3,7 +3,7 @@ import { op_log_trace, hide_window } from "ext:core/ops"; -import { clearRenderer, rerender, render } from "ext:gauntlet/renderer.js"; +import { clearRenderer, rerender, render, popView } from "ext:gauntlet/renderer.js"; import type { FC } from "react"; let latestRootUiWidget: UiWidget | undefined = undefined @@ -14,6 +14,10 @@ export function renderView(entrypointId: string, entrypointName: string, View: F latestRootUiWidget = render(entrypointId, entrypointName, "View", ); } +export function popMainView() { + popView() +} + export function renderInlineView(entrypointId: string, entrypointName: string, Handler: FC<{ text: string }>, text: string) { switch (latestRootUiRenderLocation) { case "InlineView": { diff --git a/js/react_renderer/src/renderer.ts b/js/react_renderer/src/renderer.ts index 5a380ed..c7ce931 100644 --- a/js/react_renderer/src/renderer.ts +++ b/js/react_renderer/src/renderer.ts @@ -443,6 +443,10 @@ export function rerender(view: ReactNode) { gauntletContextValue.rerender(view) } +export function popView() { + gauntletContextValue.popView() +} + export function render(entrypointId: string, entrypointName: string, renderLocation: RenderLocation, view: ReactNode): UiWidget { const hostConfig = createHostConfig(); diff --git a/js/typings/index.d.ts b/js/typings/index.d.ts index 519b2cc..afa8470 100644 --- a/js/typings/index.d.ts +++ b/js/typings/index.d.ts @@ -45,7 +45,7 @@ type MacOSDesktopSettings13AndPostData = { icon: ArrayBuffer | undefined, } -type PluginEvent = ViewEvent | NotReactsKeyboardEvent | RunCommand | RunGeneratedEntrypoint | OpenView | CloseView | OpenInlineView | RefreshSearchIndex +type PluginEvent = ViewEvent | NotReactsKeyboardEvent | RunCommand | RunGeneratedEntrypoint | OpenView | CloseView | PopView | OpenInlineView | RefreshSearchIndex type RenderLocation = "InlineView" | "View" type ViewEvent = { @@ -78,6 +78,11 @@ type CloseView = { type: "CloseView" } +type PopView = { + type: "PopView" + entrypointId: string +} + type RunCommand = { type: "RunCommand" entrypointId: string @@ -189,6 +194,7 @@ declare module "ext:gauntlet/renderer.js" { import { ReactNode } from "react"; export const render: (entrypointId: string, entrypointName: string, renderLocation: RenderLocation, component: ReactNode) => UiWidget; + export const popView: () => void; export const rerender: (component: ReactNode) => void; export const clearRenderer: () => void; } diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 8e3f75b..1c2f701 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -161,6 +161,7 @@ pub enum AppMsg { }, RunGeneratedEntrypoint(PluginId, EntrypointId, usize), RequestPluginViewOpen(PluginId, EntrypointId), + RequestPluginViewPop(PluginId, EntrypointId), RunSearchItemAction(SearchResult, usize), RunPluginAction { render_location: UiRenderLocation, @@ -463,6 +464,11 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::done(msg) } + AppMsg::RequestPluginViewPop(plugin_id, entrypoint_id) => { + state.application_manager.request_view_pop(plugin_id, entrypoint_id); + + Task::none() + } AppMsg::RequestReactViewClose(plugin_id) => { state.application_manager.request_view_close(plugin_id.clone()); state.client_context.clear_view(&plugin_id); diff --git a/rust/client/src/ui/state/mod.rs b/rust/client/src/ui/state/mod.rs index 3863dc4..432872c 100644 --- a/rust/client/src/ui/state/mod.rs +++ b/rust/client/src/ui/state/mod.rs @@ -375,7 +375,7 @@ impl Focus for GlobalState { ]) } } else { - Task::done(AppMsg::RequestPluginViewOpen(plugin_id.clone(), entrypoint_id.clone())) + Task::done(AppMsg::RequestPluginViewPop(plugin_id.clone(), entrypoint_id.clone())) } } PluginViewState::ActionPanel { .. } => Task::done(AppMsg::ToggleActionPanel { keyboard: true }), diff --git a/rust/common_plugin_runtime/src/model.rs b/rust/common_plugin_runtime/src/model.rs index f267401..643aaa6 100644 --- a/rust/common_plugin_runtime/src/model.rs +++ b/rust/common_plugin_runtime/src/model.rs @@ -21,6 +21,10 @@ pub enum JsEvent { entrypoint_id: String, }, CloseView, + PopView { + #[serde(rename = "entrypointId")] + entrypoint_id: String, + }, RunCommand { #[serde(rename = "entrypointId")] entrypoint_id: String, diff --git a/rust/server/src/model.rs b/rust/server/src/model.rs index ed2100a..ca95083 100644 --- a/rust/server/src/model.rs +++ b/rust/server/src/model.rs @@ -10,6 +10,9 @@ pub enum IntermediateUiEvent { entrypoint_id: EntrypointId, }, CloseView, + PopView { + entrypoint_id: EntrypointId, + }, RunCommand { entrypoint_id: String, }, diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 0ddc345..b0398a3 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -117,6 +117,9 @@ pub enum OnePluginCommandData { entrypoint_id: EntrypointId, }, CloseView, + PopView { + entrypoint_id: EntrypointId, + }, RunCommand { entrypoint_id: String, }, @@ -406,6 +409,9 @@ async fn event_loop( Some(IntermediateUiEvent::OpenView { entrypoint_id }) } OnePluginCommandData::CloseView => Some(IntermediateUiEvent::CloseView), + OnePluginCommandData::PopView { entrypoint_id } => { + Some(IntermediateUiEvent::PopView { entrypoint_id }) + } OnePluginCommandData::RunCommand { entrypoint_id } => { Some(IntermediateUiEvent::RunCommand { entrypoint_id }) } @@ -520,6 +526,11 @@ fn from_intermediate_to_js_event(event: IntermediateUiEvent) -> JsEvent { } } IntermediateUiEvent::CloseView => JsEvent::CloseView, + IntermediateUiEvent::PopView { entrypoint_id } => { + JsEvent::PopView { + entrypoint_id: entrypoint_id.to_string(), + } + } IntermediateUiEvent::RunCommand { entrypoint_id } => JsEvent::RunCommand { entrypoint_id }, IntermediateUiEvent::RunGeneratedEntrypoint { entrypoint_id, diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 8db4f3a..dfe8b50 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -738,6 +738,13 @@ impl ApplicationManager { Ok(shortcuts) } + pub fn request_view_pop(&self, plugin_id: PluginId, entrypoint_id: EntrypointId) { + self.send_command(PluginCommand::One { + id: plugin_id.clone(), + data: OnePluginCommandData::PopView { entrypoint_id }, + }); + } + pub fn request_view_close(&self, plugin_id: PluginId) { self.send_command(PluginCommand::One { id: plugin_id, From 32bf43438c1b72a0fc53a8b0b128b5c2405f5b7b Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 2 Aug 2025 15:40:39 +0200 Subject: [PATCH 74/91] Pin windows version to 2022 in github actions --- .github/workflows/setup-windows.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/setup-windows.yaml b/.github/workflows/setup-windows.yaml index 7da7923..51d2e8a 100644 --- a/.github/workflows/setup-windows.yaml +++ b/.github/workflows/setup-windows.yaml @@ -14,7 +14,7 @@ on: jobs: run-on-windows: - runs-on: windows-latest + runs-on: windows-2022 timeout-minutes: 60 steps: - uses: actions/checkout@v4 From e7c1f0343eb7fc267944dca93bc3402bdbec33ac Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 8 Aug 2025 22:02:41 +0200 Subject: [PATCH 75/91] Rework viewport scrolling when using keyboard navigation --- Cargo.lock | 25 +- rust/client/Cargo.toml | 1 + rust/client/src/ui/client_context.rs | 8 + rust/client/src/ui/grid_navigation.rs | 550 ---------------------- rust/client/src/ui/mod.rs | 335 ++++++------- rust/client/src/ui/scroll_handle.rs | 428 +++++++++++++---- rust/client/src/ui/search_list.rs | 13 +- rust/client/src/ui/state/main_view.rs | 171 ++++++- rust/client/src/ui/state/mod.rs | 256 +++++----- rust/client/src/ui/state/plugin_view.rs | 15 +- rust/client/src/ui/view_container.rs | 53 ++- rust/client/src/ui/widget/action_panel.rs | 162 +++---- rust/client/src/ui/widget/data.rs | 201 +++----- rust/client/src/ui/widget/data_mut.rs | 446 ++++++++++-------- rust/client/src/ui/widget/form.rs | 10 +- rust/client/src/ui/widget/grid.rs | 45 +- rust/client/src/ui/widget/list.rs | 45 +- rust/client/src/ui/widget/root.rs | 28 +- rust/client/src/ui/widget/search_bar.rs | 2 +- rust/client/src/ui/widget/state.rs | 252 ++++++++-- rust/client/src/ui/windows/mod.rs | 10 +- 21 files changed, 1492 insertions(+), 1564 deletions(-) delete mode 100644 rust/client/src/ui/grid_navigation.rs diff --git a/Cargo.lock b/Cargo.lock index c4a2323..4d80748 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4526,6 +4526,7 @@ dependencies = [ "iced", "iced_fonts", "image", + "indexmap 2.9.0", "itertools 0.13.0", "objc2-app-kit 0.2.2", "once_cell", @@ -5746,7 +5747,7 @@ dependencies = [ [[package]] name = "iced" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "iced_core", "iced_debug", @@ -5762,7 +5763,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "bitflags 2.9.1", "bytes", @@ -5780,7 +5781,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "iced_core", "iced_futures", @@ -5811,7 +5812,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "futures", "iced_core", @@ -5825,7 +5826,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "bitflags 2.9.1", "bytemuck", @@ -5845,7 +5846,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "iced_graphics", "iced_runtime", @@ -5854,7 +5855,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -5866,7 +5867,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "bytes", "iced_core", @@ -5879,7 +5880,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "bytemuck", "cosmic-text", @@ -5896,7 +5897,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "bitflags 2.9.1", "bytemuck", @@ -5916,7 +5917,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "iced_renderer", "iced_runtime", @@ -5931,7 +5932,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.13.99" -source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#126063be65dedc5f2ba3e9ac67f35a5ec9c939ce" +source = "git+https://github.com/project-gauntlet/iced.git?branch=gauntlet-0.13.1#b4ce8061d1281963360f204d93e5b2ffba582e08" dependencies = [ "iced_debug", "iced_program", diff --git a/rust/client/Cargo.toml b/rust/client/Cargo.toml index 1fbee33..004a4d8 100644 --- a/rust/client/Cargo.toml +++ b/rust/client/Cargo.toml @@ -22,6 +22,7 @@ serde_json.workspace = true image.workspace = true once_cell.workspace = true futures.workspace = true +indexmap.workspace = true # other arc-swap = "1.7.1" diff --git a/rust/client/src/ui/client_context.rs b/rust/client/src/ui/client_context.rs index f30509a..28a0892 100644 --- a/rust/client/src/ui/client_context.rs +++ b/rust/client/src/ui/client_context.rs @@ -7,6 +7,8 @@ use gauntlet_common::model::PluginId; use gauntlet_common::model::RootWidget; use gauntlet_common::model::UiRenderLocation; use gauntlet_common::model::UiWidgetId; +use iced::Task; +use iced::widget::container; use crate::model::UiViewEvent; use crate::ui::AppMsg; @@ -117,4 +119,10 @@ impl ClientContext { self.views.remove(index); } } + + pub fn set_current_focused_item(&mut self, plugin_id: PluginId, target_id: Option) -> Task { + self.get_mut_view_container(&plugin_id) + .map(|view| view.set_focused_item_id(target_id)) + .unwrap_or(Task::none()) + } } diff --git a/rust/client/src/ui/grid_navigation.rs b/rust/client/src/ui/grid_navigation.rs deleted file mode 100644 index e868160..0000000 --- a/rust/client/src/ui/grid_navigation.rs +++ /dev/null @@ -1,550 +0,0 @@ -use std::ops::Div; -use std::ops::Rem; - -#[derive(Debug)] -pub struct GridSectionData { - pub start_index: usize, - pub start_row_index: usize, - pub amount_in_section: usize, - pub width: usize, -} - -struct GridRowData { - row_index: usize, - amount_in_row: usize, - max_amount_in_row: usize, -} - -struct GridCurrentRowData { - row_index: usize, - column_index: usize, - amount_in_row: usize, - max_amount_in_row: usize, -} - -#[derive(Debug, Eq, PartialEq)] -pub struct GridItemOffset { - pub row_index: usize, - pub offset: usize, -} - -fn grid_row_data( - amount_per_section_total: Vec, - current_index: usize, -) -> (Option, GridCurrentRowData, Option) { - let mut previous_section_index: Option = None; - let mut current_section_index: usize = 0; - - for ( - index, - GridSectionData { - start_index, - amount_in_section, - .. - }, - ) in amount_per_section_total.iter().enumerate() - { - if start_index + amount_in_section >= (current_index + 1) { - current_section_index = index; - break; - } - previous_section_index = Some(index); - } - - let prev_section = previous_section_index.and_then(|index| amount_per_section_total.get(index)); - let current_section = amount_per_section_total - .get(current_section_index) - .expect("guarantied to exist"); - let next_section = amount_per_section_total.get(current_section_index + 1); - - let item_index_in_section = current_index - current_section.start_index; - let row_index_in_section = usize::div(item_index_in_section, current_section.width); - let row_index = current_section.start_row_index + row_index_in_section; - - let amount_of_rows = usize::div_ceil(current_section.amount_in_section, current_section.width); - let last_row = row_index_in_section + 1 == amount_of_rows; - - let current_row = { - if last_row { - let reminder = usize::rem(current_section.amount_in_section, current_section.width); - GridCurrentRowData { - row_index, - column_index: usize::rem(item_index_in_section, current_section.width), - amount_in_row: if reminder == 0 { current_section.width } else { reminder }, - max_amount_in_row: current_section.width, - } - } else { - GridCurrentRowData { - row_index, - column_index: usize::rem(item_index_in_section, current_section.width), - amount_in_row: current_section.width, - max_amount_in_row: current_section.width, - } - } - }; - - let prev_row = { - if row_index_in_section == 0 { - match prev_section { - None => None, - Some(prev_section) => { - let reminder = usize::rem(prev_section.amount_in_section, prev_section.width); - Some(GridRowData { - row_index: current_row.row_index - 1, - amount_in_row: if reminder == 0 { prev_section.width } else { reminder }, - max_amount_in_row: prev_section.width, - }) - } - } - } else { - Some(GridRowData { - row_index: current_row.row_index - 1, - amount_in_row: current_section.width, - max_amount_in_row: current_section.width, - }) - } - }; - - let next_row = { - if last_row { - match next_section { - None => None, - Some(next_section) => { - let reminder = usize::rem(next_section.amount_in_section, next_section.width); - Some(GridRowData { - row_index: current_row.row_index + 1, - amount_in_row: if reminder == 0 { next_section.width } else { reminder }, - max_amount_in_row: next_section.width, - }) - } - } - } else { - let amount_of_full_rows = usize::div(current_section.amount_in_section, current_section.width); - if row_index_in_section + 1 == amount_of_full_rows { - let reminder = usize::rem(current_section.amount_in_section, current_section.width); - Some(GridRowData { - row_index: current_row.row_index + 1, - amount_in_row: if reminder == 0 { current_section.width } else { reminder }, - max_amount_in_row: current_section.width, - }) - } else { - Some(GridRowData { - row_index: current_row.row_index + 1, - amount_in_row: current_section.width, - max_amount_in_row: current_section.width, - }) - } - } - }; - - (prev_row, current_row, next_row) -} - -pub fn grid_up_offset(current_index: usize, amount_per_section_total: Vec) -> Option { - let (prev_row, current_row, _next_row) = grid_row_data(amount_per_section_total, current_index); - - match prev_row { - None => None, - Some(prev_row) => { - if prev_row.amount_in_row < prev_row.max_amount_in_row { - if prev_row.amount_in_row > (current_row.column_index + 1) { - Some(GridItemOffset { - row_index: prev_row.row_index, - offset: prev_row.max_amount_in_row - prev_row.amount_in_row + (current_row.column_index + 1), - }) - } else { - Some(GridItemOffset { - row_index: prev_row.row_index, - offset: current_row.column_index + 1, - }) - } - } else { - Some(GridItemOffset { - row_index: prev_row.row_index, - offset: current_row.max_amount_in_row, - }) - } - } - } -} - -pub fn grid_down_offset( - current_index: usize, - amount_per_section_total: Vec, -) -> Option { - let (_prev_row, current_row, next_row) = grid_row_data(amount_per_section_total, current_index); - - match next_row { - None => None, - Some(next_row) => { - if (current_row.column_index + 1) > next_row.amount_in_row { - Some(GridItemOffset { - row_index: next_row.row_index, - offset: (current_row.max_amount_in_row - (current_row.column_index + 1)) + next_row.amount_in_row, - }) - } else { - Some(GridItemOffset { - row_index: next_row.row_index, - offset: current_row.amount_in_row, - }) - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn prepare_sections(data: Vec>>) -> Vec { - let mut cumulative_item_index = 0; - let mut cumulative_row_index = 0; - - data.iter() - .map(|section| { - let cumulative_item_index_at_start = cumulative_item_index; - let cumulative_row_index_at_start = cumulative_row_index; - - let amount = section.iter().map(|row| row.iter().sum::()).sum(); - - let width = section[0].len(); - - assert!(section.iter().all(|row| row.len() == width)); - for row in section { - assert!(row.iter().all(|item| *item == 0 || *item == 1)); - } - - cumulative_item_index = cumulative_item_index_at_start + amount; - cumulative_row_index = cumulative_row_index_at_start + (usize::div_ceil(amount, width)); - - GridSectionData { - start_index: cumulative_item_index_at_start, - start_row_index: cumulative_row_index_at_start, - amount_in_section: amount, - width, - } - }) - .collect() - } - - #[test] - fn grid_down_last_row() { - let sections_amount_width = prepare_sections(vec![vec![ - //fr V - vec![1, 1, 0], - ]]); - - assert_eq!(grid_down_offset(1, sections_amount_width), None) - } - - #[test] - fn grid_down_inside_1() { - let sections_amount_width = prepare_sections(vec![vec![ - //fr V - vec![1, 1, 1], - //to V - vec![1, 1, 0], - ]]); - - assert_eq!( - grid_down_offset(0, sections_amount_width), - Some(GridItemOffset { - row_index: 1, - offset: 3 - }) - ) - } - - #[test] - fn grid_down_inside_2() { - let sections_amount_width = prepare_sections(vec![vec![ - //fr V - vec![1, 1, 1], - //to V - vec![1, 1, 0], - ]]); - - assert_eq!( - grid_down_offset(1, sections_amount_width), - Some(GridItemOffset { - row_index: 1, - offset: 3 - }) - ) - } - - #[test] - fn grid_down_inside_3() { - let sections_amount_width = prepare_sections(vec![vec![ - //fr V - vec![1, 1, 1], - //to V - vec![1, 1, 0], - ]]); - - assert_eq!( - grid_down_offset(2, sections_amount_width), - Some(GridItemOffset { - row_index: 1, - offset: 2 - }) - ) - } - - #[test] - fn grid_down_inside_4() { - let sections_amount_width = prepare_sections(vec![vec![ - //fr V - vec![1, 1, 1], - //to V - vec![1, 0, 0], - ]]); - - assert_eq!( - grid_down_offset(2, sections_amount_width), - Some(GridItemOffset { - row_index: 1, - offset: 1 - }) - ) - } - - #[test] - fn grid_down_inside_5() { - let sections_amount_width = prepare_sections(vec![vec![ - vec![1, 1, 1], - //fr V - vec![1, 1, 1], - //to V - vec![1, 1, 0], - ]]); - - assert_eq!( - grid_down_offset(4, sections_amount_width), - Some(GridItemOffset { - row_index: 2, - offset: 3 - }) - ) - } - - #[test] - fn grid_down_outside_1() { - let sections_amount_width = prepare_sections(vec![ - vec![ - //fr V - vec![1, 1, 1], - ], - vec![ - //to V - vec![1, 1, 1], - ], - ]); - - assert_eq!( - grid_down_offset(1, sections_amount_width), - Some(GridItemOffset { - row_index: 1, - offset: 3 - }) - ) - } - - #[test] - fn grid_down_outside_2() { - let sections_amount_width = prepare_sections(vec![ - vec![ - //fr V - vec![1, 1, 0], - ], - vec![ - //to V - vec![1, 1, 1], - ], - ]); - - assert_eq!( - grid_down_offset(1, sections_amount_width), - Some(GridItemOffset { - row_index: 1, - offset: 2 - }) - ) - } - - #[test] - fn grid_down_outside_3() { - let sections_amount_width = prepare_sections(vec![ - vec![ - //fr V - vec![1, 1, 1], - ], - vec![ - //to V - vec![1, 1, 0], - ], - vec![vec![1, 1, 1]], - ]); - - assert_eq!( - grid_down_offset(2, sections_amount_width), - Some(GridItemOffset { - row_index: 1, - offset: 2 - }) - ) - } - - #[test] - fn grid_up_first_row() { - let sections_amount_width = prepare_sections(vec![vec![ - //fr V - vec![1, 1, 0], - ]]); - - assert_eq!(grid_up_offset(1, sections_amount_width), None) - } - - #[test] - fn grid_up_inside_1() { - let sections_amount_width = prepare_sections(vec![vec![ - //to V - vec![1, 1, 1], - //fr V - vec![1, 1, 0], - ]]); - - assert_eq!( - grid_up_offset(4, sections_amount_width), - Some(GridItemOffset { - row_index: 0, - offset: 3 - }) - ) - } - - #[test] - fn grid_up_inside_2() { - let sections_amount_width = prepare_sections(vec![vec![ - //to V - vec![1, 1, 1], - //fr V - vec![1, 1, 0], - ]]); - - assert_eq!( - grid_up_offset(3, sections_amount_width), - Some(GridItemOffset { - row_index: 0, - offset: 3 - }) - ) - } - - #[test] - fn grid_up_inside_3() { - let sections_amount_width = prepare_sections(vec![vec![ - //to V - vec![1, 1, 1], - //fr V - vec![1, 1, 1], - ]]); - - assert_eq!( - grid_up_offset(5, sections_amount_width), - Some(GridItemOffset { - row_index: 0, - offset: 3 - }) - ) - } - - #[test] - fn grid_up_outside_1() { - let sections_amount_width = prepare_sections(vec![ - vec![ - //to V - vec![1, 1, 1], - ], - vec![ - //fr V - vec![1, 1, 1], - ], - ]); - - assert_eq!( - grid_up_offset(4, sections_amount_width), - Some(GridItemOffset { - row_index: 0, - offset: 3 - }) - ) - } - - #[test] - fn grid_up_outside_2() { - let sections_amount_width = prepare_sections(vec![ - vec![ - //to V - vec![1, 1, 0], - ], - vec![ - //fr V - vec![1, 1, 1], - ], - ]); - - assert_eq!( - grid_up_offset(4, sections_amount_width), - Some(GridItemOffset { - row_index: 0, - offset: 3 - }) - ) - } - - #[test] - fn grid_up_outside_3() { - let sections_amount_width = prepare_sections(vec![ - vec![ - //to V - vec![1, 1, 0], - ], - vec![ - //fr V - vec![1, 0, 0], - ], - ]); - - assert_eq!( - grid_up_offset(2, sections_amount_width), - Some(GridItemOffset { - row_index: 0, - offset: 2 - }) - ) - } - - #[test] - fn grid_up_outside_4() { - let sections_amount_width = prepare_sections(vec![ - vec![vec![1, 1, 1]], - vec![ - //to V - vec![1, 1, 0], - ], - vec![ - //fr V - vec![1, 1, 1], - ], - ]); - - assert_eq!( - grid_up_offset(7, sections_amount_width), - Some(GridItemOffset { - row_index: 1, - offset: 3 - }) - ) - } -} diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 1c2f701..85d9330 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -66,7 +66,6 @@ use crate::ui::theme::text_input::TextInputStyle; mod client_context; mod custom_widgets; -mod grid_navigation; mod scroll_handle; mod search_list; mod settings; @@ -89,6 +88,7 @@ use crate::ui::custom_widgets::loading_bar::LoadingBar; use crate::ui::scenario_runner::ScenarioRunnerData; use crate::ui::scenario_runner::ScenarioRunnerMsg; use crate::ui::scenario_runner::handle_scenario_runner_msg; +use crate::ui::scroll_handle::ScrollContent; use crate::ui::scroll_handle::ScrollHandle; use crate::ui::server::handle_server_message; use crate::ui::settings::theme::GauntletSettingsTheme; @@ -105,8 +105,8 @@ use crate::ui::state::LoadingBarState; use crate::ui::state::MainViewState; use crate::ui::state::PluginViewData; use crate::ui::state::PluginViewState; -use crate::ui::widget::action_panel::ActionPanel; -use crate::ui::widget::action_panel::ActionPanelItem; +use crate::ui::state::main_view::search_result_action_panel; +use crate::ui::state::main_view::search_result_bot_panel_right_info; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::root::render_root; use crate::ui::windows::MainWindowState; @@ -130,7 +130,7 @@ pub struct AppModel { // state client_context: ClientContext, global_state: GlobalState, - search_results: Vec, + search_results: ScrollContent, loading_bar_state: HashMap<(PluginId, EntrypointId), ()>, hud_display: Option, } @@ -228,10 +228,10 @@ pub enum AppMsg { OnSecondaryActionMainViewNoPanelKeyboardWithoutFocus, OnAnyActionMainViewSearchResultPanelKeyboardWithFocus { search_result: SearchResult, - widget_id: UiWidgetId, + index: usize, }, OnAnyActionMainViewInlineViewPanelKeyboardWithFocus { - widget_id: UiWidgetId, + index: usize, }, OnAnyActionPluginViewNoPanelKeyboardWithFocus { plugin_id: PluginId, @@ -249,7 +249,7 @@ pub enum AppMsg { id: Option, }, OnAnyActionMainViewSearchResultPanelMouse { - widget_id: UiWidgetId, + index: usize, }, OnPrimaryActionMainViewActionPanelMouse, ResetMainViewState, @@ -280,12 +280,13 @@ pub enum AppMsg { }, WindowAction(WindowActionMsg), ResetWindowState, - ResetMainWindowScroll, + ResetMainWindowItemFocus, SetHudDisplay { display: String, }, HandleScenario(ScenarioRunnerMsg), Settings(SettingsMsg), + SetCurrentFocusedItem(Option), } pub fn run(minimized: bool, scenario_runner_data: Option) { @@ -375,7 +376,7 @@ fn new(minimized: bool, #[allow(unused)] scenario_runner_data: Option Task { } } AppMsg::PromptChanged(mut new_prompt) => { - match &mut state.global_state { + let task = match &mut state.global_state { GlobalState::MainView { focused_search_result, sub_state, @@ -571,16 +572,16 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { state.prompt = new_prompt.clone(); - focused_search_result.reset(true); - MainViewState::initial(sub_state); - } - GlobalState::ErrorView { .. } => {} - GlobalState::PluginView { .. } => {} - GlobalState::PendingPluginView { .. } => {} - } - state.search(new_prompt, true) + let first = state.search_results.ids().first().cloned(); + + focused_search_result.focus_target(first).unwrap_or(Task::none()) + } + _ => Task::none(), + }; + + Task::batch([task, state.search(new_prompt, true)]) } AppMsg::UpdateSearchResults => { match &state.global_state { @@ -590,9 +591,17 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } AppMsg::PromptSubmit => state.global_state.primary(&state.client_context, &state.search_results), AppMsg::SetSearchResults(new_search_results) => { - state.search_results = new_search_results; + let first_focus = if state.search_results.items().is_empty() { + // this is supposed to only be useful only the first time main window is opened + // hopefully doesn't cause problems elsewhere + Task::done(AppMsg::ResetMainWindowItemFocus) + } else { + Task::none() + }; - Task::none() + state.search_results.update(new_search_results); + + first_focus } AppMsg::RenderPluginUI { plugin_id, @@ -650,14 +659,17 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { *pending_plugin_view_loading_bar = LoadingBarState::Off; } - if has_children { + let focus_task = if has_children { if let UiRenderLocation::InlineView = render_location { - focused_search_result.unfocus(); + focused_search_result.focus_target(None).unwrap_or(Task::none()) + } else { + Task::none() } - } + } else { + Task::none() + }; - let command = match pending_plugin_view_data { - None => Task::none(), + let state_task = match pending_plugin_view_data { Some(pending_plugin_view_data) => { let pending_plugin_view_data = pending_plugin_view_data.clone(); GlobalState::plugin( @@ -669,13 +681,16 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { false, ) } + None => Task::none(), }; - if let UiRenderLocation::InlineView = render_location { - Task::batch([command, state.request_inline_view_shortcuts()]) + let shortcuts_task = if let UiRenderLocation::InlineView = render_location { + state.request_inline_view_shortcuts() } else { - command - } + Task::none() + }; + + Task::batch([focus_task, state_task, shortcuts_task]) } GlobalState::ErrorView { .. } => Task::none(), GlobalState::PluginView { plugin_view_data, .. } => { @@ -914,11 +929,15 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { MainViewState::None => { if let Some(search_item) = focused_search_result.get(&state.search_results) { if search_item.entrypoint_actions.len() > 0 { - MainViewState::search_result_action_panel(sub_state, keyboard); + MainViewState::search_result_action_panel(sub_state, keyboard, search_item.clone()); } } else { - if let Some(_) = state.client_context.get_first_inline_view_container() { - MainViewState::inline_result_action_panel(sub_state, keyboard); + if let Some(view) = state.client_context.get_first_inline_view_container() { + MainViewState::inline_result_action_panel( + sub_state, + keyboard, + ScrollContent::new_with_ids(view.get_action_widgets_with_ids()), + ); } } } @@ -930,7 +949,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } } - GlobalState::ErrorView { .. } => {} GlobalState::PluginView { plugin_view_data, sub_state, @@ -947,6 +965,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { PluginViewState::ActionPanel { .. } => PluginViewState::initial(sub_state), } } + GlobalState::ErrorView { .. } => {} GlobalState::PendingPluginView { .. } => {} } @@ -964,18 +983,13 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { AppMsg::OnSecondaryActionMainViewNoPanelKeyboardWithFocus { search_result } => { Task::done(AppMsg::RunSearchItemAction(search_result, 1)) } - AppMsg::OnAnyActionMainViewSearchResultPanelKeyboardWithFocus { - search_result, - widget_id, - } => { - let index = widget_id; - + AppMsg::OnAnyActionMainViewSearchResultPanelKeyboardWithFocus { search_result, index } => { Task::batch([ Task::done(AppMsg::RunSearchItemAction(search_result, index)), Task::done(AppMsg::ResetMainViewState), ]) } - AppMsg::OnAnyActionMainViewInlineViewPanelKeyboardWithFocus { widget_id } => { + AppMsg::OnAnyActionMainViewInlineViewPanelKeyboardWithFocus { index } => { match state.client_context.get_first_inline_view_container() { Some(container) => { let plugin_id = container.plugin_id(); @@ -985,7 +999,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::done(AppMsg::RunPluginAction { render_location: UiRenderLocation::InlineView, plugin_id, - widget_id, + widget_id: index, id: None, }), ]) @@ -1039,7 +1053,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { AppMsg::OnAnyActionMainViewNoPanelKeyboardAtIndex { index } => { if let Some(container) = state.client_context.get_first_inline_view_container() { let plugin_id = container.plugin_id(); - let action_ids = container.get_action_ids(); + let action_ids = container.get_action_widgets(); match action_ids.get(index) { Some(widget_id) => { @@ -1058,7 +1072,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } } - AppMsg::OnAnyActionMainViewSearchResultPanelMouse { widget_id } => { + AppMsg::OnAnyActionMainViewSearchResultPanelMouse { index } => { match &mut state.global_state { GlobalState::MainView { focused_search_result, @@ -1072,7 +1086,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { let search_result = search_result.clone(); Task::done(AppMsg::OnAnyActionMainViewSearchResultPanelKeyboardWithFocus { search_result, - widget_id, + index, }) } else { Task::none() @@ -1219,14 +1233,15 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { responder, } => handle_server_message(state, request_data, responder), AppMsg::ResetWindowState => state.reset_window_state(), - AppMsg::ResetMainWindowScroll => { - if let GlobalState::MainView { - focused_search_result, .. - } = &state.global_state - { - focused_search_result.scroll_to(0) - } else { - Task::none() + AppMsg::ResetMainWindowItemFocus => { + match &mut state.global_state { + GlobalState::MainView { + focused_search_result, .. + } => { + let first = state.search_results.ids().first().cloned(); + focused_search_result.focus_target(first).unwrap_or(Task::none()) + } + _ => Task::none(), } } AppMsg::SetHudDisplay { display } => { @@ -1245,6 +1260,42 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { AppMsg::Settings(msg) => { update_settings(&mut state.settings_window_state, &state.global_hotkey_manager, msg).map(AppMsg::Settings) } + AppMsg::SetCurrentFocusedItem(target_item_id) => { + match &mut state.global_state { + GlobalState::MainView { + focused_search_result, + sub_state, + .. + } => { + match sub_state { + MainViewState::None => focused_search_result.set_current_focused_item(target_item_id), + MainViewState::SearchResultActionPanel { scroll_handle, .. } => { + scroll_handle.set_current_focused_item(target_item_id) + } + MainViewState::InlineViewActionPanel { scroll_handle, .. } => { + scroll_handle.set_current_focused_item(target_item_id) + } + } + } + GlobalState::PluginView { + sub_state, + plugin_view_data, + .. + } => { + match sub_state { + PluginViewState::None => { + state + .client_context + .set_current_focused_item(plugin_view_data.plugin_id.clone(), target_item_id) + } + PluginViewState::ActionPanel { scroll_handle } => { + scroll_handle.set_current_focused_item(target_item_id) + } + } + } + _ => Task::none(), + } + } } } @@ -1534,124 +1585,10 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { let (primary_action, action_panel) = if let Some(search_item) = focused_search_result.get(&state.search_results) { - let primary_shortcut = PhysicalShortcut { - physical_key: PhysicalKey::Enter, - modifier_shift: false, - modifier_control: false, - modifier_alt: false, - modifier_meta: false, - }; - - let secondary_shortcut = PhysicalShortcut { - physical_key: PhysicalKey::Enter, - modifier_shift: true, - modifier_control: false, - modifier_alt: false, - modifier_meta: false, - }; - - let create_static = - |label: &str, primary_shortcut: PhysicalShortcut, secondary_shortcut: PhysicalShortcut| { - let mut actions: Vec<_> = search_item - .entrypoint_actions - .iter() - .enumerate() - .map(|(index, action)| { - let physical_shortcut = if index == 0 { - Some(secondary_shortcut.clone()) - } else { - action.shortcut.clone() - }; - - ActionPanelItem::Action { - label: action.label.clone(), - widget_id: index, - physical_shortcut, - } - }) - .collect(); - - let primary_action_widget_id = 0; - - if actions.len() == 0 { - ( - Some((label.to_string(), primary_action_widget_id, primary_shortcut)), - None, - ) - } else { - let label = label.to_string(); - - let primary_action = ActionPanelItem::Action { - label: label.clone(), - widget_id: primary_action_widget_id, - physical_shortcut: Some(primary_shortcut.clone()), - }; - - actions.insert(0, primary_action); - - let action_panel = ActionPanel { - title: Some(search_item.entrypoint_name.clone()), - items: actions, - }; - - ( - Some((label, primary_action_widget_id, primary_shortcut)), - Some(action_panel), - ) - } - }; - - let create_generated = - |label: &str, primary_shortcut: PhysicalShortcut, secondary_shortcut: PhysicalShortcut| { - let label = search_item - .entrypoint_actions - .first() - .map(|action| action.label.clone()) - .unwrap_or_else(|| label.to_string()); // should never happen, because there is always at least one action - - let actions: Vec<_> = search_item - .entrypoint_actions - .iter() - .enumerate() - .map(|(index, action)| { - let physical_shortcut = match index { - 0 => Some(primary_shortcut.clone()), - 1 => Some(secondary_shortcut.clone()), - _ => action.shortcut.clone(), - }; - - ActionPanelItem::Action { - label: action.label.clone(), - widget_id: index, - physical_shortcut, - } - }) - .collect(); - - let primary_action_widget_id = 0; - - let action_panel = ActionPanel { - title: Some(search_item.entrypoint_name.clone()), - items: actions, - }; - - ( - Some((label, primary_action_widget_id, primary_shortcut)), - Some(action_panel), - ) - }; - - match search_item.entrypoint_type { - SearchResultEntrypointType::Command => { - create_static("Run Command", primary_shortcut, secondary_shortcut) - } - SearchResultEntrypointType::View => { - create_static("Open View", primary_shortcut, secondary_shortcut) - } - SearchResultEntrypointType::Generated => { - create_generated("Run Command", primary_shortcut, secondary_shortcut) - } - } + ( + Some(search_result_bot_panel_right_info(search_item)), + search_result_action_panel(search_item), + ) } else { match state.client_context.get_first_inline_view_action_panel() { None => (None, None), @@ -1659,15 +1596,7 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { match action_panel.find_first() { None => (None, None), Some((label, widget_id)) => { - let shortcut = PhysicalShortcut { - physical_key: PhysicalKey::Enter, - modifier_shift: false, - modifier_control: false, - modifier_alt: false, - modifier_meta: false, - }; - - (Some((label, widget_id, shortcut)), Some(action_panel)) + (Some((label, widget_id, primary_shortcut())), Some(action_panel)) } } } @@ -1693,17 +1622,15 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { None::<&ScrollHandle>, "", || AppMsg::ToggleActionPanel { keyboard: false }, - |_widget_id| { - // widget_id here is always 0 + |_index| { + // index here is always 0 AppMsg::OnPrimaryActionMainViewActionPanelMouse }, - |_widget_id| AppMsg::Noop, + |_index| AppMsg::Noop, || AppMsg::Noop, ) } - MainViewState::SearchResultActionPanel { - focused_action_item, .. - } => { + MainViewState::SearchResultActionPanel { scroll_handle, .. } => { render_root( true, input, @@ -1712,20 +1639,18 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { content, primary_action, action_panel, - Some(focused_action_item), + Some(scroll_handle), "", || AppMsg::ToggleActionPanel { keyboard: false }, - |_widget_id| { - // widget_id here is always 0 + |_index| { + // index here is always 0 AppMsg::OnPrimaryActionMainViewActionPanelMouse }, - |widget_id| AppMsg::OnAnyActionMainViewSearchResultPanelMouse { widget_id }, + |index| AppMsg::OnAnyActionMainViewSearchResultPanelMouse { index }, || AppMsg::Noop, ) } - MainViewState::InlineViewActionPanel { - focused_action_item, .. - } => { + MainViewState::InlineViewActionPanel { scroll_handle, .. } => { render_root( true, input, @@ -1734,14 +1659,14 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { content, primary_action, action_panel, - Some(focused_action_item), + Some(scroll_handle), "", || AppMsg::ToggleActionPanel { keyboard: false }, - |_widget_id| { + |_index| { // widget_id here is always 0 AppMsg::OnPrimaryActionMainViewActionPanelMouse }, - |widget_id| AppMsg::OnAnyActionMainViewInlineViewPanelKeyboardWithFocus { widget_id }, + |index| AppMsg::OnAnyActionMainViewInlineViewPanelKeyboardWithFocus { index }, || AppMsg::Noop, ) } @@ -2227,3 +2152,23 @@ impl AppModel { focus(search_field_id.clone()) } } + +pub fn primary_shortcut() -> PhysicalShortcut { + PhysicalShortcut { + physical_key: PhysicalKey::Enter, + modifier_shift: false, + modifier_control: false, + modifier_alt: false, + modifier_meta: false, + } +} + +pub fn secondary_shortcut() -> PhysicalShortcut { + PhysicalShortcut { + physical_key: PhysicalKey::Enter, + modifier_shift: true, + modifier_control: false, + modifier_alt: false, + modifier_meta: false, + } +} diff --git a/rust/client/src/ui/scroll_handle.rs b/rust/client/src/ui/scroll_handle.rs index 60ef917..ec60288 100644 --- a/rust/client/src/ui/scroll_handle.rs +++ b/rust/client/src/ui/scroll_handle.rs @@ -1,117 +1,369 @@ +use std::mem; + +use iced::Rectangle; use iced::Task; +use iced::Vector; +use iced::advanced::widget; +use iced::advanced::widget::Operation; +use iced::advanced::widget::operate; +use iced::advanced::widget::operation::Outcome; +use iced::advanced::widget::operation::Scrollable; +use iced::widget::container; +use iced::widget::scrollable; use iced::widget::scrollable::AbsoluteOffset; -use iced::widget::scrollable::Id; use iced::widget::scrollable::scroll_to; +use indexmap::IndexMap; +use itertools::Itertools; use crate::ui::AppMsg; -pub const ESTIMATED_MAIN_LIST_ITEM_HEIGHT: f32 = 38.8; -pub const ESTIMATED_ACTION_ITEM_HEIGHT: f32 = 38.8; // TODO +pub struct ScrollContent { + items: IndexMap, +} + +impl ScrollContent { + pub fn new(items: Vec) -> Self { + let mut content = Self { items: IndexMap::new() }; + content.update(items); + content + } + + pub fn new_with_ids(items: IndexMap) -> Self { + Self { items } + } + + pub fn update(&mut self, new_items: Vec) { + let items = mem::replace(&mut self.items, IndexMap::new()); + + let mut keys: Vec<_> = items.into_keys().collect(); + + keys.resize_with(new_items.len(), || container::Id::unique()); + + self.items = keys.into_iter().zip(new_items.into_iter()).collect(); + } + + pub fn items(&self) -> &IndexMap { + &self.items + } + + pub fn ids(&self) -> Vec { + self.items.iter().map(|(id, _)| id.clone()).collect() + } +} #[derive(Clone, Debug)] pub struct ScrollHandle { - pub scrollable_id: Id, - pub index: Option, - offset: usize, - rows_per_view: usize, - item_height: f32, + pub scrollable_id: scrollable::Id, + pub current_item_id: Option, } impl ScrollHandle { - pub fn new(first_focused: bool, item_height: f32, rows_per_view: usize) -> ScrollHandle { + pub fn new(current_item_id: Option) -> ScrollHandle { ScrollHandle { - scrollable_id: Id::unique(), - index: if first_focused { Some(0) } else { None }, - offset: 0, - rows_per_view, - item_height, + scrollable_id: scrollable::Id::unique(), + current_item_id, } } - pub fn reset(&mut self, first_focused: bool) { - self.index = if first_focused { Some(0) } else { None }; - self.offset = 0; + pub fn get<'a, T>(&self, items: &'a ScrollContent) -> Option<&'a T> { + self.get_by_id(items, self.current_item_id.clone()) } - pub fn unfocus(&mut self) { - self.index = None; - } - - pub fn get<'a, T>(&self, search_results: &'a [T]) -> Option<&'a T> { - match self.index { - None => None, - Some(index) => search_results.get(index), - } - } - - pub fn focus_next(&mut self, total_item_amount: usize) -> Option> { - match self.focus_next_in(total_item_amount, 1) { - None => None, - Some(index) => Some(self.scroll_to(index)), - } - } - - pub fn focus_next_in(&mut self, total_item_amount: usize, amount: usize) -> Option { - self.offset = if self.offset < self.rows_per_view { - self.offset + 1 - } else { - self.rows_per_view + pub fn get_by_id<'a, T>( + &self, + items: &'a ScrollContent, + target_item_id: Option, + ) -> Option<&'a T> { + let Some(id) = &target_item_id else { + return None; }; - match self.index.as_mut() { - None => { - // focus first - if total_item_amount > 0 { - self.index = Some(0); - - Some(0) - } else { - None - } - } - Some(index) => { - // focus next if there is an item - let new_index = *index + amount; - if new_index < total_item_amount { - *index = new_index; - - Some(*index) - } else { - None - } - } - } + items.items.get(id) } - pub fn focus_previous(&mut self) -> Option> { - match self.focus_previous_in(1) { - None => None, - Some(index) => Some(self.scroll_to(index)), - } + pub fn get_index(&self, items: &ScrollContent) -> Option { + let Some(id) = &self.current_item_id else { + return None; + }; + + items.items.get_index_of(id) } - pub fn focus_previous_in(&mut self, amount: usize) -> Option { - self.offset = if self.offset > 1 { self.offset - 1 } else { 1 }; - - match self.index.as_mut() { - None => None, - Some(index) => { - match index.checked_sub(amount) { - // basically a check if result is >= 0 - Some(new_index) => { - *index = new_index; - - Some(new_index) - } - None => None, - } - } - } + pub fn grid_focus_down(&mut self, grid: Vec>) -> (Option, Option>) { + self.grid_focus_target(DownScrollBehavior, grid) } - pub fn scroll_to(&self, row_index: usize) -> Task { - let pos_y = row_index as f32 * self.item_height - (self.offset as f32 * self.item_height); + pub fn grid_focus_up(&mut self, grid: Vec>) -> (Option, Option>) { + self.grid_focus_target(UpScrollBehavior, grid) + } - scroll_to(self.scrollable_id.clone(), AbsoluteOffset { x: 0.0, y: pos_y }) + pub fn grid_focus_left(&mut self, grid: Vec>) -> (Option, Option>) { + self.grid_focus_target(LeftScrollBehavior, grid) + } + + pub fn grid_focus_right(&mut self, grid: Vec>) -> (Option, Option>) { + self.grid_focus_target(RightScrollBehavior, grid) + } + + pub fn list_focus_down(&mut self, list: Vec) -> (Option, Option>) { + let grid = list.into_iter().map(|item| vec![item]).collect(); + self.grid_focus_target(DownScrollBehavior, grid) + } + + pub fn list_focus_up(&mut self, list: Vec) -> (Option, Option>) { + let grid = list.into_iter().map(|item| vec![item]).collect(); + self.grid_focus_target(UpScrollBehavior, grid) + } + + fn grid_focus_target( + &mut self, + behavior: impl ScrollBehavior, + grid: Vec>, + ) -> (Option, Option>) { + let Some(current_item_id) = &self.current_item_id else { + return (None, self.focus_target(behavior.unfocused(grid))); + }; + + let Some((row_index, row)) = grid.iter().find_position(|row| row.contains(¤t_item_id)) else { + return (self.current_item_id.clone(), None); + }; + + let Some(column_index) = row.iter().position(|item| item == current_item_id) else { + return (self.current_item_id.clone(), None); + }; + + let target_item_id = behavior.focused(grid, current_item_id.clone(), row_index, column_index); + + (target_item_id.clone(), self.focus_target(target_item_id)) + } + + pub fn focus_target(&mut self, target_item_id: Option) -> Option> { + if self.current_item_id == target_item_id { + return None; + } + + let task = focus_target(self.scrollable_id.clone(), target_item_id.clone(), 40.) + .chain(Task::done(AppMsg::SetCurrentFocusedItem(target_item_id))); + + Some(task) + } + + pub fn set_current_focused_item(&mut self, target_item_id: Option) -> Task { + self.current_item_id = target_item_id.clone(); + Task::none() } } + +trait ScrollBehavior { + fn unfocused(&self, grid: Vec>) -> Option; + fn focused( + &self, + grid: Vec>, + current_item_id: container::Id, + row_index: usize, + column_index: usize, + ) -> Option; +} + +struct DownScrollBehavior; +impl ScrollBehavior for DownScrollBehavior { + fn unfocused(&self, grid: Vec>) -> Option { + grid.first() + .map(|row| row.first().map(|item| item.clone())) + .unwrap_or_default() + } + + fn focused( + &self, + grid: Vec>, + current_item_id: container::Id, + row_index: usize, + column_index: usize, + ) -> Option { + let Some(next_row) = grid.get(row_index + 1) else { + return Some(current_item_id); + }; + + let Some(next_item) = next_row.get(column_index) else { + return next_row.iter().last().cloned(); + }; + + Some(next_item.clone()) + } +} + +struct UpScrollBehavior; +impl ScrollBehavior for UpScrollBehavior { + fn unfocused(&self, _grid: Vec>) -> Option { + None + } + + fn focused( + &self, + grid: Vec>, + current_item_id: container::Id, + row_index: usize, + column_index: usize, + ) -> Option { + let Some(next_row_index) = row_index.checked_sub(1) else { + return None; + }; + + let Some(next_row) = grid.get(next_row_index) else { + return Some(current_item_id); + }; + + let Some(next_item) = next_row.get(column_index) else { + return next_row.iter().last().cloned(); + }; + + Some(next_item.clone()) + } +} + +struct LeftScrollBehavior; +impl ScrollBehavior for LeftScrollBehavior { + fn unfocused(&self, _grid: Vec>) -> Option { + None + } + + fn focused( + &self, + grid: Vec>, + current_item_id: container::Id, + row_index: usize, + column_index: usize, + ) -> Option { + let Some(next_row) = grid.get(row_index) else { + return Some(current_item_id); + }; + + let Some(next_column_index) = column_index.checked_sub(1) else { + return Some(current_item_id); + }; + + let Some(next_item) = next_row.get(next_column_index) else { + return Some(current_item_id); + }; + + Some(next_item.clone()) + } +} + +struct RightScrollBehavior; +impl ScrollBehavior for RightScrollBehavior { + fn unfocused(&self, _grid: Vec>) -> Option { + None + } + + fn focused( + &self, + grid: Vec>, + current_item_id: container::Id, + row_index: usize, + column_index: usize, + ) -> Option { + let Some(next_row) = grid.get(row_index) else { + return Some(current_item_id); + }; + + let Some(next_item) = next_row.get(column_index + 1) else { + return Some(current_item_id); + }; + + Some(next_item.clone()) + } +} + +fn focus_target(scrollable_id: scrollable::Id, target_item_id: Option, padding: f32) -> Task { + let Some(target_item_id) = target_item_id else { + return scroll_to(scrollable_id.clone(), AbsoluteOffset::default()); + }; + + struct CalculateScrollToIdOffset { + scrollable: widget::Id, + target: widget::Id, + viewport_rectangle: Option, + viewport_translation: Option, + target_rectangle: Option, + padding: f32, + } + + impl Operation for CalculateScrollToIdOffset { + fn container( + &mut self, + id: Option<&widget::Id>, + bounds: Rectangle, + operate_on_children: &mut dyn FnMut(&mut dyn Operation), + ) { + if Some(&self.target) == id { + self.target_rectangle = Some(bounds) + } else { + operate_on_children(self); + } + } + + fn scrollable( + &mut self, + id: Option<&widget::Id>, + bounds: Rectangle, + _content_bounds: Rectangle, + translation: Vector, + _state: &mut dyn Scrollable, + ) { + if Some(&self.scrollable) == id { + self.viewport_rectangle = Some(bounds); + self.viewport_translation = Some(translation); + } + } + + fn finish(&self) -> Outcome { + let Some(target_rectangle) = self.target_rectangle else { + return Outcome::None; + }; + + let Some(viewport_rectangle) = self.viewport_rectangle else { + return Outcome::None; + }; + + let Some(viewport_translation) = self.viewport_translation else { + return Outcome::None; + }; + + let r_x = target_rectangle.x; + let r_y = target_rectangle.y; + let r_w = target_rectangle.width; + let r_h = target_rectangle.height; + let t_x = viewport_translation.x; + let t_y = viewport_translation.y; + let v_w = viewport_rectangle.width; + let v_h = viewport_rectangle.height; + let v_x = viewport_rectangle.x; + let v_y = viewport_rectangle.y; + + let pad = self.padding; + + let offset_x = t_x.max(r_x + r_w - (v_x + v_w) + pad).min(r_x - v_x - pad); + let offset_y = t_y.max(r_y + r_h - (v_y + v_h) + pad).min(r_y - v_y - pad); + + let offset = AbsoluteOffset { + x: offset_x, + y: offset_y, + }; + + Outcome::Some(offset) + } + } + + let operation = CalculateScrollToIdOffset { + scrollable: scrollable_id.clone().into(), + target: target_item_id.clone().into(), + viewport_rectangle: None, + viewport_translation: None, + target_rectangle: None, + padding, + }; + + let scrollable_id = scrollable_id.clone(); + operate(operation).then(move |offset| scroll_to(scrollable_id.clone(), offset)) +} diff --git a/rust/client/src/ui/search_list.rs b/rust/client/src/ui/search_list.rs index e74b863..388cbf8 100644 --- a/rust/client/src/ui/search_list.rs +++ b/rust/client/src/ui/search_list.rs @@ -17,6 +17,7 @@ use iced::widget::row; use iced::widget::text; use iced::widget::text::Shaping; +use crate::ui::scroll_handle::ScrollContent; use crate::ui::scroll_handle::ScrollHandle; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; @@ -29,12 +30,12 @@ use crate::ui::widget::accessories::render_icon_accessory; use crate::ui::widget::accessories::render_text_accessory; pub fn search_list<'a>( - search_results: &'a [SearchResult], + search_results: &'a ScrollContent, focused_search_result: &ScrollHandle, ) -> Element<'a, SearchResult> { let items: Vec> = search_results + .items() .iter() - .enumerate() .map(|(index, search_result)| { let entrypoint_name: Element<_> = text(&search_result.entrypoint_name).shaping(Shaping::Advanced).into(); let entrypoint_name: Element<_> = container(entrypoint_name).themed(ContainerStyle::MainListItemText); @@ -135,7 +136,7 @@ pub fn search_list<'a>( let button_content: Element<_> = row(button_content).align_y(Alignment::Center).into(); - let style = match focused_search_result.index { + let style = match &focused_search_result.current_item_id { None => ButtonStyle::MainListItem, Some(focused_index) => { if focused_index == index { @@ -146,10 +147,12 @@ pub fn search_list<'a>( } }; - button(button_content) + let content = button(button_content) .width(Length::Fill) .on_press(search_result.clone()) - .themed(style) + .themed(style); + + container(content).id(index.clone()).into() }) .collect(); diff --git a/rust/client/src/ui/state/main_view.rs b/rust/client/src/ui/state/main_view.rs index 9801596..4817009 100644 --- a/rust/client/src/ui/state/main_view.rs +++ b/rust/client/src/ui/state/main_view.rs @@ -1,15 +1,27 @@ -use crate::ui::scroll_handle::ESTIMATED_ACTION_ITEM_HEIGHT; +use gauntlet_common::model::PhysicalShortcut; +use gauntlet_common::model::SearchResult; +use gauntlet_common::model::SearchResultEntrypointAction; +use gauntlet_common::model::SearchResultEntrypointType; +use gauntlet_common::model::UiWidgetId; + +use crate::ui::primary_shortcut; +use crate::ui::scroll_handle::ScrollContent; use crate::ui::scroll_handle::ScrollHandle; +use crate::ui::secondary_shortcut; +use crate::ui::widget::action_panel::ActionPanel; +use crate::ui::widget::action_panel::ActionPanelItem; +use crate::ui::widget::action_panel::action_item_container_id; pub enum MainViewState { None, SearchResultActionPanel { - // ephemeral state - focused_action_item: ScrollHandle, + search_result: SearchResult, + entrypoint_actions: ScrollContent, + scroll_handle: ScrollHandle, }, InlineViewActionPanel { - // ephemeral state - focused_action_item: ScrollHandle, + scroll_handle: ScrollHandle, + actions: ScrollContent, }, } @@ -22,15 +34,156 @@ impl MainViewState { *prev_state = Self::None } - pub fn search_result_action_panel(prev_state: &mut MainViewState, focus_first: bool) { + pub fn search_result_action_panel(prev_state: &mut MainViewState, focus_first: bool, search_result: SearchResult) { + let first_action_item = if focus_first { + Some(action_item_container_id(0)) + } else { + None + }; + + let items = search_result + .entrypoint_actions + .iter() + .enumerate() + .map(|(index, action)| (action_item_container_id(index), action.clone())) + .collect(); + *prev_state = Self::SearchResultActionPanel { - focused_action_item: ScrollHandle::new(focus_first, ESTIMATED_ACTION_ITEM_HEIGHT, 7), + search_result: search_result.clone(), + entrypoint_actions: ScrollContent::new_with_ids(items), + scroll_handle: ScrollHandle::new(first_action_item), } } - pub fn inline_result_action_panel(prev_state: &mut MainViewState, focus_first: bool) { + pub fn inline_result_action_panel( + prev_state: &mut MainViewState, + focus_first: bool, + actions: ScrollContent, + ) { + let first_action_item = if focus_first { + Some(action_item_container_id(0)) + } else { + None + }; + *prev_state = Self::InlineViewActionPanel { - focused_action_item: ScrollHandle::new(focus_first, ESTIMATED_ACTION_ITEM_HEIGHT, 7), + scroll_handle: ScrollHandle::new(first_action_item), + actions, } } } + +pub fn search_result_action_panel(search_item: &SearchResult) -> Option { + fn create_static(search_item: &SearchResult, label: &str) -> Option { + let mut actions: Vec<_> = search_item + .entrypoint_actions + .iter() + .enumerate() + .map(|(index, action)| { + let physical_shortcut = if index == 0 { + Some(secondary_shortcut()) + } else { + action.shortcut.clone() + }; + + ActionPanelItem::Action { + label: action.label.clone(), + container_id: action_item_container_id(index + 1), + widget_id: index, + physical_shortcut, + } + }) + .collect(); + + let primary_action_widget_id = 0; + + if actions.is_empty() { + None + } else { + let label = label.to_string(); + + let primary_action = ActionPanelItem::Action { + label: label.clone(), + container_id: action_item_container_id(0), + widget_id: primary_action_widget_id, + physical_shortcut: Some(primary_shortcut()), + }; + + actions.insert(0, primary_action); + + let action_panel = ActionPanel { + title: Some(search_item.entrypoint_name.clone()), + items: actions, + }; + + Some(action_panel) + } + } + + fn create_generated(search_item: &SearchResult) -> Option { + let actions: Vec<_> = search_item + .entrypoint_actions + .iter() + .enumerate() + .map(|(index, action)| { + let physical_shortcut = match index { + 0 => Some(primary_shortcut()), + 1 => Some(secondary_shortcut()), + _ => action.shortcut.clone(), + }; + + ActionPanelItem::Action { + label: action.label.clone(), + container_id: action_item_container_id(index), + widget_id: index, + physical_shortcut, + } + }) + .collect(); + + let action_panel = ActionPanel { + title: Some(search_item.entrypoint_name.clone()), + items: actions, + }; + + Some(action_panel) + } + + match search_item.entrypoint_type { + SearchResultEntrypointType::Command => create_static(search_item, "Run Command"), + SearchResultEntrypointType::View => create_static(search_item, "Open View"), + SearchResultEntrypointType::Generated => create_generated(search_item), + } +} + +pub fn search_result_bot_panel_right_info(search_item: &SearchResult) -> (String, usize, PhysicalShortcut) { + fn create_static(search_item: &SearchResult, label: &str) -> (String, usize, PhysicalShortcut) { + let primary_action_widget_id = 0; + + if search_item.entrypoint_actions.is_empty() { + (label.to_string(), primary_action_widget_id, primary_shortcut()) + } else { + let label = label.to_string(); + + (label, primary_action_widget_id, primary_shortcut()) + } + } + + fn create_generated(search_item: &SearchResult, label: &str) -> (String, usize, PhysicalShortcut) { + let label = search_item + .entrypoint_actions + .first() + .map(|action| action.label.clone()) + .unwrap_or_else(|| label.to_string()); // should never happen, because there is always at least one action + + let primary_action_widget_id = 0; + + (label, primary_action_widget_id, primary_shortcut()) + } + + match search_item.entrypoint_type { + SearchResultEntrypointType::Command => create_static(search_item, "Run Command"), + SearchResultEntrypointType::View => create_static(search_item, "Open View"), + SearchResultEntrypointType::Generated => create_generated(search_item, "Run Command"), + } +} diff --git a/rust/client/src/ui/state/mod.rs b/rust/client/src/ui/state/mod.rs index 432872c..77d6435 100644 --- a/rust/client/src/ui/state/mod.rs +++ b/rust/client/src/ui/state/mod.rs @@ -1,4 +1,4 @@ -mod main_view; +pub mod main_view; mod plugin_view; use std::collections::HashMap; @@ -13,7 +13,7 @@ use iced::widget::text_input::focus; use crate::ui::AppMsg; use crate::ui::client_context::ClientContext; -use crate::ui::scroll_handle::ESTIMATED_MAIN_LIST_ITEM_HEIGHT; +use crate::ui::scroll_handle::ScrollContent; use crate::ui::scroll_handle::ScrollHandle; pub use crate::ui::state::main_view::MainViewState; pub use crate::ui::state::plugin_view::PluginViewState; @@ -84,7 +84,7 @@ impl GlobalState { pub fn new(search_field_id: text_input::Id) -> GlobalState { GlobalState::MainView { search_field_id, - focused_search_result: ScrollHandle::new(true, ESTIMATED_MAIN_LIST_ITEM_HEIGHT, 7), + focused_search_result: ScrollHandle::new(None), sub_state: MainViewState::new(), pending_plugin_view_data: None, pending_plugin_view_loading_bar: LoadingBarState::Off, @@ -104,6 +104,7 @@ impl GlobalState { close_window_on_esc, } } + pub fn new_pending_plugin(pending_plugin_view_data: PluginViewData) -> GlobalState { GlobalState::PendingPluginView { pending_plugin_view_data, @@ -174,19 +175,19 @@ impl GlobalState { } pub trait Focus { - fn primary(&mut self, client_context: &ClientContext, focus_list: &[T]) -> Task; - fn secondary(&mut self, client_context: &ClientContext, focus_list: &[T]) -> Task; + fn primary(&mut self, client_context: &ClientContext, focus_list: &ScrollContent) -> Task; + fn secondary(&mut self, client_context: &ClientContext, focus_list: &ScrollContent) -> Task; fn back(&mut self, client_context: &ClientContext) -> Task; fn next(&mut self, client_context: &ClientContext) -> Task; fn previous(&mut self, client_context: &ClientContext) -> Task; - fn up(&mut self, client_context: &mut ClientContext, focus_list: &[T]) -> Task; - fn down(&mut self, client_context: &mut ClientContext, focus_list: &[T]) -> Task; - fn left(&mut self, client_context: &mut ClientContext, focus_list: &[T]) -> Task; - fn right(&mut self, client_context: &mut ClientContext, focus_list: &[T]) -> Task; + fn up(&mut self, client_context: &mut ClientContext, focus_list: &ScrollContent) -> Task; + fn down(&mut self, client_context: &mut ClientContext, focus_list: &ScrollContent) -> Task; + fn left(&mut self, client_context: &mut ClientContext, focus_list: &ScrollContent) -> Task; + fn right(&mut self, client_context: &mut ClientContext, focus_list: &ScrollContent) -> Task; } impl Focus for GlobalState { - fn primary(&mut self, client_context: &ClientContext, focus_list: &[SearchResult]) -> Task { + fn primary(&mut self, client_context: &ClientContext, focus_list: &ScrollContent) -> Task { match self { GlobalState::MainView { focused_search_result, @@ -195,38 +196,34 @@ impl Focus for GlobalState { } => { match sub_state { MainViewState::None => { - if let Some(search_result) = focused_search_result.get(focus_list) { - let search_result = search_result.clone(); - Task::done(AppMsg::OnPrimaryActionMainViewNoPanel { search_result }) - } else { - Task::done(AppMsg::OnPrimaryActionMainViewNoPanelKeyboardWithoutFocus) - } + let Some(search_result) = focused_search_result.get(focus_list) else { + return Task::done(AppMsg::OnPrimaryActionMainViewNoPanelKeyboardWithoutFocus); + }; + + Task::done(AppMsg::OnPrimaryActionMainViewNoPanel { + search_result: search_result.clone(), + }) } MainViewState::SearchResultActionPanel { - focused_action_item, .. + search_result, + entrypoint_actions, + scroll_handle, } => { - match focused_action_item.index { - None => Task::none(), - Some(widget_id) => { - if let Some(search_result) = focused_search_result.get(&focus_list) { - let search_result = search_result.clone(); - Task::done(AppMsg::OnAnyActionMainViewSearchResultPanelKeyboardWithFocus { - search_result, - widget_id, - }) - } else { - Task::none() - } - } - } + let Some(index) = scroll_handle.get_index(entrypoint_actions) else { + return Task::none(); + }; + + Task::done(AppMsg::OnAnyActionMainViewSearchResultPanelKeyboardWithFocus { + search_result: search_result.clone(), + index, + }) } - MainViewState::InlineViewActionPanel { focused_action_item } => { - match focused_action_item.index { - None => Task::none(), - Some(widget_id) => { - Task::done(AppMsg::OnAnyActionMainViewInlineViewPanelKeyboardWithFocus { widget_id }) - } - } + MainViewState::InlineViewActionPanel { scroll_handle, actions } => { + let Some(index) = scroll_handle.get_index(actions) else { + return Task::none(); + }; + + Task::done(AppMsg::OnAnyActionMainViewInlineViewPanelKeyboardWithFocus { index }) } } } @@ -239,35 +236,31 @@ impl Focus for GlobalState { return Task::none(); }; - let action_ids = view.get_action_ids(); + let action_ids = view.get_action_widgets_with_ids(); let focused_item_id = view.get_focused_item_id(); match sub_state { PluginViewState::None => { - if let Some(widget_id) = action_ids.get(0) { - let widget_id = *widget_id; - Task::done(AppMsg::OnAnyActionPluginViewNoPanelKeyboardWithFocus { - plugin_id: plugin_view_data.plugin_id.clone(), - widget_id, - id: focused_item_id, - }) - } else { - Task::none() - } + let Some((_, widget_id)) = action_ids.first() else { + return Task::none(); + }; + + Task::done(AppMsg::OnAnyActionPluginViewNoPanelKeyboardWithFocus { + plugin_id: plugin_view_data.plugin_id.clone(), + widget_id: *widget_id, + id: focused_item_id, + }) } - PluginViewState::ActionPanel { - focused_action_item, .. - } => { - if let Some(widget_id) = focused_action_item.get(&action_ids) { - let widget_id = *widget_id; - Task::done(AppMsg::OnAnyActionPluginViewAnyPanelKeyboardWithFocus { - plugin_id: plugin_view_data.plugin_id.clone(), - widget_id, - id: focused_item_id, - }) - } else { - Task::none() - } + PluginViewState::ActionPanel { scroll_handle } => { + let Some(&widget_id) = scroll_handle.get(&ScrollContent::new_with_ids(action_ids)) else { + return Task::none(); + }; + + Task::done(AppMsg::OnAnyActionPluginViewAnyPanelKeyboardWithFocus { + plugin_id: plugin_view_data.plugin_id.clone(), + widget_id, + id: focused_item_id, + }) } } } @@ -276,7 +269,7 @@ impl Focus for GlobalState { } } - fn secondary(&mut self, client_context: &ClientContext, focus_list: &[SearchResult]) -> Task { + fn secondary(&mut self, client_context: &ClientContext, focus_list: &ScrollContent) -> Task { match self { GlobalState::MainView { focused_search_result, @@ -285,12 +278,13 @@ impl Focus for GlobalState { } => { match sub_state { MainViewState::None => { - if let Some(search_result) = focused_search_result.get(focus_list) { - let search_result = search_result.clone(); - Task::done(AppMsg::OnSecondaryActionMainViewNoPanelKeyboardWithFocus { search_result }) - } else { - Task::done(AppMsg::OnSecondaryActionMainViewNoPanelKeyboardWithoutFocus) - } + let Some(search_result) = focused_search_result.get(focus_list) else { + return Task::done(AppMsg::OnSecondaryActionMainViewNoPanelKeyboardWithoutFocus); + }; + + Task::done(AppMsg::OnSecondaryActionMainViewNoPanelKeyboardWithFocus { + search_result: search_result.clone(), + }) } MainViewState::SearchResultActionPanel { .. } | MainViewState::InlineViewActionPanel { .. } => { // secondary does nothing when action panel is opened @@ -303,25 +297,24 @@ impl Focus for GlobalState { sub_state, .. } => { - let Some(container) = client_context.get_view_container(&plugin_view_data.plugin_id) else { + let Some(view) = client_context.get_view_container(&plugin_view_data.plugin_id) else { return Task::none(); }; - let action_ids = container.get_action_ids(); - let focused_item_id = container.get_focused_item_id(); + let action_ids = view.get_action_widgets(); + let focused_item_id = view.get_focused_item_id(); match sub_state { PluginViewState::None => { - if let Some(widget_id) = action_ids.get(1) { - let widget_id = *widget_id; - Task::done(AppMsg::OnAnyActionPluginViewNoPanelKeyboardWithFocus { - plugin_id: plugin_view_data.plugin_id.clone(), - widget_id, - id: focused_item_id, - }) - } else { - Task::none() - } + let Some(widget_id) = action_ids.get(1) else { + return Task::none(); + }; + + Task::done(AppMsg::OnAnyActionPluginViewNoPanelKeyboardWithFocus { + plugin_id: plugin_view_data.plugin_id.clone(), + widget_id: *widget_id, + id: focused_item_id, + }) } PluginViewState::ActionPanel { .. } => { // secondary does nothing when action panel is opened @@ -401,7 +394,7 @@ impl Focus for GlobalState { GlobalState::PendingPluginView { .. } => Task::none(), } } - fn up(&mut self, client_context: &mut ClientContext, _focus_list: &[SearchResult]) -> Task { + fn up(&mut self, client_context: &mut ClientContext, focus_list: &ScrollContent) -> Task { match self { GlobalState::MainView { focused_search_result, @@ -409,12 +402,24 @@ impl Focus for GlobalState { .. } => { match sub_state { - MainViewState::None => focused_search_result.focus_previous().unwrap_or_else(|| Task::none()), - MainViewState::SearchResultActionPanel { focused_action_item } => { - focused_action_item.focus_previous().unwrap_or_else(|| Task::none()) + MainViewState::None => { + let (_, task) = focused_search_result.list_focus_up(focus_list.ids()); + + task.unwrap_or(Task::none()) } - MainViewState::InlineViewActionPanel { focused_action_item } => { - focused_action_item.focus_previous().unwrap_or_else(|| Task::none()) + MainViewState::SearchResultActionPanel { + entrypoint_actions, + scroll_handle, + .. + } => { + let (_, task) = scroll_handle.list_focus_up(entrypoint_actions.ids()); + + task.unwrap_or(Task::none()) + } + MainViewState::InlineViewActionPanel { scroll_handle, actions } => { + let (_, task) = scroll_handle.list_focus_up(actions.ids()); + + task.unwrap_or(Task::none()) } } } @@ -430,15 +435,19 @@ impl Focus for GlobalState { match sub_state { PluginViewState::None => view.focus_up(), - PluginViewState::ActionPanel { focused_action_item } => { - focused_action_item.focus_previous().unwrap_or_else(|| Task::none()) + PluginViewState::ActionPanel { scroll_handle } => { + let action_ids = view.get_action_widgets_ids(); + + let (_, task) = scroll_handle.list_focus_up(action_ids); + + task.unwrap_or(Task::none()) } } } GlobalState::PendingPluginView { .. } => Task::none(), } } - fn down(&mut self, client_context: &mut ClientContext, focus_list: &[SearchResult]) -> Task { + fn down(&mut self, client_context: &mut ClientContext, focus_list: &ScrollContent) -> Task { match self { GlobalState::MainView { focused_search_result, @@ -447,40 +456,23 @@ impl Focus for GlobalState { } => { match sub_state { MainViewState::None => { - if focus_list.len() != 0 { - focused_search_result - .focus_next(focus_list.len()) - .unwrap_or_else(|| Task::none()) - } else { - Task::none() - } + let (_, task) = focused_search_result.list_focus_down(focus_list.ids()); + + task.unwrap_or(Task::none()) } - MainViewState::SearchResultActionPanel { focused_action_item } => { - if let Some(search_item) = focused_search_result.get(focus_list) { - if search_item.entrypoint_actions.len() != 0 { - focused_action_item - .focus_next(search_item.entrypoint_actions.len()) - .unwrap_or_else(|| Task::none()) - } else { - Task::none() - } - } else { - Task::none() - } + MainViewState::SearchResultActionPanel { + entrypoint_actions, + scroll_handle, + .. + } => { + let (_, task) = scroll_handle.list_focus_down(entrypoint_actions.ids()); + + task.unwrap_or(Task::none()) } - MainViewState::InlineViewActionPanel { focused_action_item } => { - match client_context.get_first_inline_view_action_panel() { - Some(action_panel) => { - if action_panel.action_count() != 0 { - focused_action_item - .focus_next(action_panel.action_count()) - .unwrap_or_else(|| Task::none()) - } else { - Task::none() - } - } - None => Task::none(), - } + MainViewState::InlineViewActionPanel { scroll_handle, actions } => { + let (_, task) = scroll_handle.list_focus_down(actions.ids()); + + task.unwrap_or(Task::none()) } } } @@ -496,23 +488,19 @@ impl Focus for GlobalState { match sub_state { PluginViewState::None => view.focus_down(), - PluginViewState::ActionPanel { focused_action_item } => { - let action_ids = view.get_action_ids(); + PluginViewState::ActionPanel { scroll_handle } => { + let action_ids = view.get_action_widgets_ids(); - if action_ids.len() != 0 { - focused_action_item - .focus_next(action_ids.len()) - .unwrap_or_else(|| Task::none()) - } else { - Task::none() - } + let (_, task) = scroll_handle.list_focus_down(action_ids); + + task.unwrap_or(Task::none()) } } } GlobalState::PendingPluginView { .. } => Task::none(), } } - fn left(&mut self, client_context: &mut ClientContext, _focus_list: &[SearchResult]) -> Task { + fn left(&mut self, client_context: &mut ClientContext, _focus_list: &ScrollContent) -> Task { match self { GlobalState::PluginView { plugin_view_data, @@ -533,7 +521,7 @@ impl Focus for GlobalState { GlobalState::PendingPluginView { .. } => Task::none(), } } - fn right(&mut self, client_context: &mut ClientContext, _focus_list: &[SearchResult]) -> Task { + fn right(&mut self, client_context: &mut ClientContext, _focus_list: &ScrollContent) -> Task { match self { GlobalState::PluginView { plugin_view_data, diff --git a/rust/client/src/ui/state/plugin_view.rs b/rust/client/src/ui/state/plugin_view.rs index 995d157..3e2d5c1 100644 --- a/rust/client/src/ui/state/plugin_view.rs +++ b/rust/client/src/ui/state/plugin_view.rs @@ -1,13 +1,10 @@ -use crate::ui::scroll_handle::ESTIMATED_ACTION_ITEM_HEIGHT; use crate::ui::scroll_handle::ScrollHandle; +use crate::ui::widget::action_panel::action_item_container_id; #[derive(Debug, Clone)] pub enum PluginViewState { None, - ActionPanel { - // ephemeral state - focused_action_item: ScrollHandle, - }, + ActionPanel { scroll_handle: ScrollHandle }, } impl PluginViewState { @@ -20,8 +17,14 @@ impl PluginViewState { } pub fn action_panel(prev_state: &mut PluginViewState, focus_first: bool) { + let first_action_item = if focus_first { + Some(action_item_container_id(0)) + } else { + None + }; + *prev_state = Self::ActionPanel { - focused_action_item: ScrollHandle::new(focus_first, ESTIMATED_ACTION_ITEM_HEIGHT, 7), + scroll_handle: ScrollHandle::new(first_action_item), } } } diff --git a/rust/client/src/ui/view_container.rs b/rust/client/src/ui/view_container.rs index e53926f..0ae47bb 100644 --- a/rust/client/src/ui/view_container.rs +++ b/rust/client/src/ui/view_container.rs @@ -10,21 +10,24 @@ use gauntlet_common::model::RootWidget; use gauntlet_common::model::UiRenderLocation; use gauntlet_common::model::UiWidgetId; use iced::Task; +use iced::widget::container; +use indexmap::IndexMap; use crate::model::UiViewEvent; use crate::ui::AppMsg; use crate::ui::state::PluginViewState; use crate::ui::theme::Element; use crate::ui::widget::action_panel::ActionPanel; +use crate::ui::widget::action_panel::action_item_container_id; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::data_mut::ComponentWidgetsMut; use crate::ui::widget::events::ComponentWidgetEvent; -use crate::ui::widget::state::ComponentWidgetState; +use crate::ui::widget::state::ComponentWidgetStateContainer; use crate::ui::widget::state::create_state; pub struct PluginViewContainer { root_widget: Option>, - state: HashMap, + state: ComponentWidgetStateContainer, data: HashMap>, render_location: UiRenderLocation, plugin_id: PluginId, @@ -37,7 +40,7 @@ impl PluginViewContainer { pub fn new(render_location: UiRenderLocation, plugin_id: PluginId, entrypoint_id: EntrypointId) -> Self { Self { root_widget: None, - state: HashMap::new(), + state: ComponentWidgetStateContainer(HashMap::new()), data: HashMap::new(), render_location, plugin_id, @@ -76,8 +79,8 @@ impl PluginViewContainer { // so this way we use already existing values but remove state for removed widgets let old_state = mem::replace(&mut self.state, create_state(&container)); - for (key, value) in old_state.into_iter() { - match self.state.entry(key) { + for (key, value) in old_state.0.into_iter() { + match self.state.0.entry(key) { Entry::Occupied(mut entry) => { // copy over old value, but only if type of the widget didn't change // if it did change, the widget state is reset @@ -106,7 +109,7 @@ impl PluginViewContainer { pub fn handle_event(&mut self, plugin_id: PluginId, event: ComponentWidgetEvent) -> Option { let widget_id = event.widget_id(); - event.handle(plugin_id, self.state.get_mut(&widget_id)) + event.handle(plugin_id, self.state.0.get_mut(&widget_id)) } pub fn render_root_widget<'a>( @@ -127,11 +130,13 @@ impl PluginViewContainer { } pub fn append_text(&mut self, text: &str) -> Task { - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).append_text(text) + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &mut self.data, &self.plugin_id) + .append_text(text) } pub fn backspace_text(&mut self) -> Task { - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).backspace_text() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &mut self.data, &self.plugin_id) + .backspace_text() } pub fn focus_search_bar(&self, widget_id: UiWidgetId) -> Task { @@ -139,11 +144,24 @@ impl PluginViewContainer { } pub fn toggle_action_panel(&mut self) { - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).toggle_action_panel() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &mut self.data, &self.plugin_id) + .toggle_action_panel() } - pub fn get_action_ids(&self) -> Vec { - ComponentWidgets::new(&self.root_widget, &self.state, &self.data).get_action_ids() + pub fn get_action_widgets_with_ids(&self) -> IndexMap { + self.get_action_widgets() + .into_iter() + .enumerate() + .map(|(index, id)| (action_item_container_id(index), id)) + .collect() + } + + pub fn get_action_widgets_ids(&self) -> Vec { + self.get_action_widgets_with_ids().keys().cloned().collect() + } + + pub fn get_action_widgets(&self) -> Vec { + ComponentWidgets::new(&self.root_widget, &self.state, &self.data).get_action_widgets() } pub fn get_focused_item_id(&self) -> Option { @@ -155,18 +173,23 @@ impl PluginViewContainer { } pub fn focus_up(&mut self) -> Task { - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).focus_up() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &mut self.data, &self.plugin_id).focus_up() } pub fn focus_down(&mut self) -> Task { - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).focus_down() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &mut self.data, &self.plugin_id).focus_down() } pub fn focus_left(&mut self) -> Task { - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).focus_left() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &mut self.data, &self.plugin_id).focus_left() } pub fn focus_right(&mut self) -> Task { - ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &self.plugin_id).focus_right() + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &mut self.data, &self.plugin_id).focus_right() + } + + pub fn set_focused_item_id(&mut self, target_id: Option) -> Task { + ComponentWidgetsMut::new(&mut self.root_widget, &mut self.state, &mut self.data, &self.plugin_id) + .set_focused_item_id(target_id) } } diff --git a/rust/client/src/ui/widget/action_panel.rs b/rust/client/src/ui/widget/action_panel.rs index 4e64ac9..2baa711 100644 --- a/rust/client/src/ui/widget/action_panel.rs +++ b/rust/client/src/ui/widget/action_panel.rs @@ -1,12 +1,10 @@ use std::cell::Cell; use std::collections::HashMap; -use gauntlet_common::model::ActionPanelSectionWidget; use gauntlet_common::model::ActionPanelSectionWidgetOrderedMembers; use gauntlet_common::model::ActionPanelWidget; use gauntlet_common::model::ActionPanelWidgetOrderedMembers; use gauntlet_common::model::ActionWidget; -use gauntlet_common::model::PhysicalKey; use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::UiWidgetId; use gauntlet_common_ui::shortcut_to_text; @@ -24,7 +22,9 @@ use iced::widget::row; use iced::widget::scrollable; use iced::widget::text; +use crate::ui::primary_shortcut; use crate::ui::scroll_handle::ScrollHandle; +use crate::ui::secondary_shortcut; use crate::ui::theme::Element; use crate::ui::theme::ThemableWidget; use crate::ui::theme::button::ButtonStyle; @@ -40,10 +40,6 @@ pub struct ActionPanel { } impl ActionPanel { - pub fn action_count(&self) -> usize { - self.items.iter().map(|item| item.action_count()).sum() - } - pub fn find_first(&self) -> Option<(String, UiWidgetId)> { ActionPanelItem::find_first(&self.items) } @@ -53,6 +49,7 @@ impl ActionPanel { pub enum ActionPanelItem { Action { label: String, + container_id: container::Id, widget_id: UiWidgetId, physical_shortcut: Option, }, @@ -62,14 +59,11 @@ pub enum ActionPanelItem { }, } -impl ActionPanelItem { - fn action_count(&self) -> usize { - match self { - ActionPanelItem::Action { .. } => 1, - ActionPanelItem::ActionSection { items, .. } => items.iter().map(|item| item.action_count()).sum(), - } - } +pub fn action_item_container_id(index: usize) -> container::Id { + container::Id::new(format!("gauntlet-entrypoint-action-{}", index)) +} +impl ActionPanelItem { fn find_first(items: &[ActionPanelItem]) -> Option<(String, UiWidgetId)> { for item in items { match item { @@ -90,70 +84,73 @@ pub fn convert_action_panel( action_panel: &Option, action_shortcuts: &HashMap, ) -> Option { - match action_panel { - Some(ActionPanelWidget { content, title, .. }) => { - fn action_widget_to_action( - ActionWidget { __id__, id, label }: &ActionWidget, - action_shortcuts: &HashMap, - ) -> ActionPanelItem { - let physical_shortcut: Option = - id.as_ref().map(|id| action_shortcuts.get(id)).flatten().cloned(); + let Some(ActionPanelWidget { content, title, .. }) = action_panel else { + return None; + }; - ActionPanelItem::Action { - label: label.clone(), - widget_id: *__id__, - physical_shortcut, + fn action_widget_to_action( + widget: &ActionWidget, + action_shortcuts: &HashMap, + index_counter: &Cell, + ) -> ActionPanelItem { + let physical_shortcut = widget.id.as_ref().map(|id| action_shortcuts.get(id)).flatten().cloned(); + + let container_id = action_item_container_id(index_counter.get()); + + index_counter.set(index_counter.get() + 1); + + ActionPanelItem::Action { + label: widget.label.clone(), + container_id, + widget_id: widget.__id__, + physical_shortcut, + } + } + + let index_counter = Cell::new(0); + + let items = content + .ordered_members + .iter() + .map(|members| { + match members { + ActionPanelWidgetOrderedMembers::Action(widget) => { + action_widget_to_action(widget, action_shortcuts, &index_counter) + } + ActionPanelWidgetOrderedMembers::ActionPanelSection(widget) => { + let section_items = widget + .content + .ordered_members + .iter() + .map(|members| { + match members { + ActionPanelSectionWidgetOrderedMembers::Action(widget) => { + action_widget_to_action(widget, action_shortcuts, &index_counter) + } + } + }) + .collect(); + + ActionPanelItem::ActionSection { + title: widget.title.clone(), + items: section_items, + } } } + }) + .collect(); - let items = content - .ordered_members - .iter() - .map(|members| { - match members { - ActionPanelWidgetOrderedMembers::Action(widget) => { - action_widget_to_action(widget, action_shortcuts) - } - ActionPanelWidgetOrderedMembers::ActionPanelSection(ActionPanelSectionWidget { - content, - title, - .. - }) => { - let section_items = content - .ordered_members - .iter() - .map(|members| { - match members { - ActionPanelSectionWidgetOrderedMembers::Action(widget) => { - action_widget_to_action(widget, action_shortcuts) - } - } - }) - .collect(); - - ActionPanelItem::ActionSection { - title: title.clone(), - items: section_items, - } - } - } - }) - .collect(); - - Some(ActionPanel { - title: title.clone(), - items, - }) - } - _ => None, - } + Some(ActionPanel { + title: title.clone(), + items, + }) } fn render_action_panel_items<'a, T: 'a + Clone>( root: bool, title: Option, items: Vec, - action_panel_focus_index: Option, + action_panel_focus_id: Option, on_action_click: &dyn Fn(UiWidgetId) -> T, index_counter: &Cell, ) -> Vec> { @@ -189,30 +186,13 @@ fn render_action_panel_items<'a, T: 'a + Clone>( match item { ActionPanelItem::Action { label, + container_id, widget_id, physical_shortcut, } => { let physical_shortcut = match index_counter.get() { - 0 => { - Some(PhysicalShortcut { - // primary - physical_key: PhysicalKey::Enter, - modifier_shift: false, - modifier_control: false, - modifier_alt: false, - modifier_meta: false, - }) - } - 1 => { - Some(PhysicalShortcut { - // secondary - physical_key: PhysicalKey::Enter, - modifier_shift: true, - modifier_control: false, - modifier_alt: false, - modifier_meta: false, - }) - } + 0 => Some(primary_shortcut()), + 1 => Some(secondary_shortcut()), _ => physical_shortcut, }; @@ -229,10 +209,10 @@ fn render_action_panel_items<'a, T: 'a + Clone>( text(label).shaping(Shaping::Advanced).into() }; - let style = match action_panel_focus_index { + let style = match &action_panel_focus_id { None => ButtonStyle::Action, Some(focused_index) => { - if focused_index == index_counter.get() { + if focused_index == &container_id { ButtonStyle::ActionFocused } else { ButtonStyle::Action @@ -254,7 +234,7 @@ fn render_action_panel_items<'a, T: 'a + Clone>( false, title, items, - action_panel_focus_index, + action_panel_focus_id.clone(), on_action_click, index_counter, ); @@ -278,7 +258,7 @@ pub fn render_action_panel<'a, T: 'a + Clone, F: Fn(UiWidgetId) -> T>( true, action_panel.title, action_panel.items, - action_panel_scroll_handle.index, + action_panel_scroll_handle.current_item_id.clone(), &on_action_click, &Cell::new(0), ); diff --git a/rust/client/src/ui/widget/data.rs b/rust/client/src/ui/widget/data.rs index 4adfa50..1b659d6 100644 --- a/rust/client/src/ui/widget/data.rs +++ b/rust/client/src/ui/widget/data.rs @@ -15,32 +15,32 @@ use gauntlet_common::model::RootWidget; use gauntlet_common::model::RootWidgetMembers; use gauntlet_common::model::UiWidgetId; use iced::Task; +use iced::widget::container; use iced::widget::text_input; +use indexmap::IndexMap; use crate::ui::AppMsg; -use crate::ui::grid_navigation::GridSectionData; +use crate::ui::scroll_handle::ScrollContent; use crate::ui::scroll_handle::ScrollHandle; use crate::ui::widget::action_panel::ActionPanel; use crate::ui::widget::action_panel::convert_action_panel; +use crate::ui::widget::data_mut::ComponentWidgetsMut; use crate::ui::widget::events::ComponentWidgetEvent; -use crate::ui::widget::grid::grid_width; -use crate::ui::widget::state::CheckboxState; -use crate::ui::widget::state::ComponentWidgetState; -use crate::ui::widget::state::RootState; -use crate::ui::widget::state::SelectState; +use crate::ui::widget::state::ComponentWidgetStateContainer; +use crate::ui::widget::state::ScrollableRootState; use crate::ui::widget::state::TextFieldState; #[derive(Debug)] pub struct ComponentWidgets<'b> { pub root_widget: &'b Option>, - pub state: &'b HashMap, + pub state: &'b ComponentWidgetStateContainer, pub data: &'b HashMap>, } impl<'b> ComponentWidgets<'b> { pub fn new( root_widget: &'b Option>, - state: &'b HashMap, + state: &'b ComponentWidgetStateContainer, data: &'b HashMap>, ) -> ComponentWidgets<'b> { Self { @@ -50,57 +50,17 @@ impl<'b> ComponentWidgets<'b> { } } - pub fn text_field_state(&self, widget_id: UiWidgetId) -> &TextFieldState { - let state = self.state.get(&widget_id).expect(&format!( - "requested state should always be present for id: {}", - widget_id - )); - - match state { - ComponentWidgetState::TextField(state) => state, - _ => panic!("TextFieldState expected, {:?} found", state), - } - } - - pub fn checkbox_state(&self, widget_id: UiWidgetId) -> &CheckboxState { - let state = self.state.get(&widget_id).expect(&format!( - "requested state should always be present for id: {}", - widget_id - )); - - match state { - ComponentWidgetState::Checkbox(state) => state, - _ => panic!("CheckboxState expected, {:?} found", state), - } - } - - pub fn select_state(&self, widget_id: UiWidgetId) -> &SelectState { - let state = self.state.get(&widget_id).expect(&format!( - "requested state should always be present for id: {}", - widget_id - )); - - match state { - ComponentWidgetState::Select(state) => state, - _ => panic!("SelectState expected, {:?} found", state), - } - } - - pub fn root_state(&self, widget_id: UiWidgetId) -> &RootState { - let state = self.state.get(&widget_id).expect(&format!( - "requested state should always be present for id: {}", - widget_id - )); - - match state { - ComponentWidgetState::Root(state) => state, - _ => panic!("RootState expected, {:?} found", state), + pub fn from_mut<'a>(widgets: &'b ComponentWidgetsMut<'a>) -> Self { + Self { + root_widget: &widgets.root_widget, + state: &widgets.state, + data: &widgets.data, } } } impl<'b> ComponentWidgets<'b> { - pub fn get_action_ids(&self) -> Vec { + pub fn get_action_widgets(&self) -> Vec { let Some(root_widget) = &self.root_widget else { return vec![]; }; @@ -155,94 +115,29 @@ impl<'b> ComponentWidgets<'b> { RootWidgetMembers::Form(_) => None, RootWidgetMembers::Inline(_) => None, RootWidgetMembers::List(widget) => { - let RootState { focused_item, .. } = self.root_state(widget.__id__); + let ScrollableRootState { + scroll_handle: focused_item, + .. + } = self.state.scrollable_root_state(widget.__id__); - ComponentWidgets::list_focused_item_id(focused_item, widget) + self.list_focused_item_id(focused_item, focused_item.current_item_id.clone(), widget) } RootWidgetMembers::Grid(widget) => { - let RootState { focused_item, .. } = self.root_state(widget.__id__); + let ScrollableRootState { + scroll_handle: focused_item, + .. + } = self.state.scrollable_root_state(widget.__id__); - ComponentWidgets::grid_focused_item_id(focused_item, widget) + self.grid_focused_item_id(focused_item, focused_item.current_item_id.clone(), widget) } } } pub fn focus_search_bar(&self, widget_id: UiWidgetId) -> Task { - let TextFieldState { text_input_id, .. } = self.text_field_state(widget_id); + let TextFieldState { text_input_id, .. } = self.state.text_field_state(widget_id); text_input::focus(text_input_id.clone()) } - - pub fn grid_section_sizes(grid_widget: &GridWidget) -> Vec { - let mut amount_per_section: Vec = vec![]; - let mut pending_section_size = 0; - - let mut cumulative_item_index = 0; - let mut cumulative_row_index = 0; - - let mut cumulative_item_index_at_start = cumulative_item_index; - let mut cumulative_row_index_at_start = cumulative_row_index; - - for members in &grid_widget.content.ordered_members { - match &members { - GridWidgetOrderedMembers::GridItem(_) => { - pending_section_size = pending_section_size + 1; - } - GridWidgetOrderedMembers::GridSection(widget) => { - if pending_section_size > 0 { - let width = grid_width(&grid_widget.columns); - amount_per_section.push(GridSectionData { - start_index: cumulative_item_index_at_start, - start_row_index: cumulative_row_index_at_start, - amount_in_section: pending_section_size, - width, - }); - - cumulative_item_index = cumulative_item_index + pending_section_size; - cumulative_row_index = - cumulative_row_index_at_start + (usize::div_ceil(pending_section_size, width)); - - cumulative_item_index_at_start = cumulative_item_index; - cumulative_row_index_at_start = cumulative_row_index; - - pending_section_size = 0; - } - - let section_amount = widget - .content - .ordered_members - .iter() - .filter(|members| matches!(members, GridSectionWidgetOrderedMembers::GridItem(_))) - .count(); - - let width = grid_width(&widget.columns); - amount_per_section.push(GridSectionData { - start_index: cumulative_item_index_at_start, - start_row_index: cumulative_row_index_at_start, - amount_in_section: section_amount, - width, - }); - - cumulative_item_index = cumulative_item_index + section_amount; - cumulative_row_index = cumulative_row_index_at_start + (usize::div_ceil(section_amount, width)); - - cumulative_item_index_at_start = cumulative_item_index; - cumulative_row_index_at_start = cumulative_row_index; - } - } - } - - if pending_section_size > 0 { - amount_per_section.push(GridSectionData { - start_index: cumulative_item_index_at_start, - start_row_index: cumulative_row_index_at_start, - amount_in_section: pending_section_size, - width: grid_width(&grid_widget.columns), - }); - } - - amount_per_section - } } impl<'b> ComponentWidgets<'b> { @@ -274,19 +169,26 @@ impl<'b> ComponentWidgets<'b> { AppMsg::FocusPluginViewSearchBar { plugin_id, widget_id } } - pub fn list_focused_item_id(focused_item: &ScrollHandle, widget: &ListWidget) -> Option { - let mut items = vec![]; + pub fn list_focused_item_id( + &self, + scroll_handle: &ScrollHandle, + target_item_id: Option, + widget: &ListWidget, + ) -> Option { + let mut items = IndexMap::new(); for members in &widget.content.ordered_members { match &members { ListWidgetOrderedMembers::ListItem(item) => { - items.push(&item.id); + let state = self.state.scrollable_item_state(item.__id__); + items.insert(state.id.clone(), &item.id); } ListWidgetOrderedMembers::ListSection(section) => { for members in §ion.content.ordered_members { match &members { ListSectionWidgetOrderedMembers::ListItem(item) => { - items.push(&item.id); + let state = self.state.scrollable_item_state(item.__id__); + items.insert(state.id.clone(), &item.id); } } } @@ -294,18 +196,20 @@ impl<'b> ComponentWidgets<'b> { } } - match focused_item.get(&items) { + match scroll_handle.get_by_id(&ScrollContent::new_with_ids(items), target_item_id) { None => None, Some(item_id) => Some(item_id.to_string()), } } pub fn list_item_focus_event( + &self, plugin_id: PluginId, - focused_item: &ScrollHandle, + scroll_handle: &ScrollHandle, + target_item_id: Option, widget: &ListWidget, ) -> Task { - let widget_event = match ComponentWidgets::list_focused_item_id(focused_item, widget) { + let widget_event = match self.list_focused_item_id(scroll_handle, target_item_id, widget) { None => { ComponentWidgetEvent::FocusListItem { list_widget_id: widget.__id__, @@ -326,19 +230,26 @@ impl<'b> ComponentWidgets<'b> { }) } - pub fn grid_focused_item_id(focused_item: &ScrollHandle, widget: &GridWidget) -> Option { - let mut items = vec![]; + pub fn grid_focused_item_id( + &self, + scroll_handle: &ScrollHandle, + target_item_id: Option, + widget: &GridWidget, + ) -> Option { + let mut items = IndexMap::new(); for members in &widget.content.ordered_members { match &members { GridWidgetOrderedMembers::GridItem(item) => { - items.push(&item.id); + let state = self.state.scrollable_item_state(item.__id__); + items.insert(state.id.clone(), &item.id); } GridWidgetOrderedMembers::GridSection(section) => { for members in §ion.content.ordered_members { match &members { GridSectionWidgetOrderedMembers::GridItem(item) => { - items.push(&item.id); + let state = self.state.scrollable_item_state(item.__id__); + items.insert(state.id.clone(), &item.id); } } } @@ -346,18 +257,20 @@ impl<'b> ComponentWidgets<'b> { } } - match focused_item.get(&items) { + match scroll_handle.get_by_id(&ScrollContent::new_with_ids(items), target_item_id) { None => None, Some(item_id) => Some(item_id.to_string()), } } pub fn grid_item_focus_event( + &self, plugin_id: PluginId, - focused_item: &ScrollHandle, + scroll_handle: &ScrollHandle, + target_item_id: Option, widget: &GridWidget, ) -> Task { - let widget_event = match ComponentWidgets::grid_focused_item_id(focused_item, widget) { + let widget_event = match self.grid_focused_item_id(scroll_handle, target_item_id, widget) { None => { ComponentWidgetEvent::FocusGridItem { grid_widget_id: widget.__id__, diff --git a/rust/client/src/ui/widget/data_mut.rs b/rust/client/src/ui/widget/data_mut.rs index d3dd584..7e41780 100644 --- a/rust/client/src/ui/widget/data_mut.rs +++ b/rust/client/src/ui/widget/data_mut.rs @@ -1,83 +1,51 @@ use std::collections::HashMap; +use std::mem; use std::sync::Arc; use gauntlet_common::model::GridSectionWidgetOrderedMembers; +use gauntlet_common::model::GridWidget; use gauntlet_common::model::GridWidgetOrderedMembers; use gauntlet_common::model::ListSectionWidgetOrderedMembers; +use gauntlet_common::model::ListWidget; use gauntlet_common::model::ListWidgetOrderedMembers; use gauntlet_common::model::PluginId; use gauntlet_common::model::RootWidget; use gauntlet_common::model::RootWidgetMembers; use gauntlet_common::model::UiWidgetId; use iced::Task; +use iced::advanced::widget::operate; +use iced::advanced::widget::operation::focusable::unfocus; +use iced::widget::container; use iced::widget::text_input; +use itertools::Itertools; use crate::ui::AppMsg; -use crate::ui::grid_navigation::grid_down_offset; -use crate::ui::grid_navigation::grid_up_offset; use crate::ui::widget::data::ComponentWidgets; -use crate::ui::widget::state::ComponentWidgetState; -use crate::ui::widget::state::RootState; -use crate::ui::widget::state::TextFieldState; +use crate::ui::widget::grid::grid_width; +use crate::ui::widget::state::ComponentWidgetStateContainer; #[derive(Debug)] pub struct ComponentWidgetsMut<'b> { pub root_widget: &'b mut Option>, - pub state: &'b mut HashMap, + pub state: &'b mut ComponentWidgetStateContainer, + pub data: &'b HashMap>, pub plugin_id: PluginId, } impl<'b> ComponentWidgetsMut<'b> { pub fn new( root_widget: &'b mut Option>, - state: &'b mut HashMap, + state: &'b mut ComponentWidgetStateContainer, + data: &'b mut HashMap>, plugin_id: &PluginId, ) -> ComponentWidgetsMut<'b> { Self { root_widget, state, + data, plugin_id: plugin_id.clone(), } } - - #[allow(unused)] - pub fn text_field_state_mut(&mut self, widget_id: UiWidgetId) -> &mut TextFieldState { - Self::text_field_state_mut_on_state(&mut self.state, widget_id) - } - - pub fn text_field_state_mut_on_state( - state: &mut HashMap, - widget_id: UiWidgetId, - ) -> &mut TextFieldState { - let state = state.get_mut(&widget_id).expect(&format!( - "requested state should always be present for id: {}", - widget_id - )); - - match state { - ComponentWidgetState::TextField(state) => state, - _ => panic!("TextFieldState expected, {:?} found", state), - } - } - - pub fn root_state_mut(&mut self, widget_id: UiWidgetId) -> &mut RootState { - Self::root_state_mut_on_field(&mut self.state, widget_id) - } - - pub fn root_state_mut_on_field( - state: &mut HashMap, - widget_id: UiWidgetId, - ) -> &mut RootState { - let state = state.get_mut(&widget_id).expect(&format!( - "requested state should always be present for id: {}", - widget_id - )); - - match state { - ComponentWidgetState::Root(state) => state, - _ => panic!("RootState expected, {:?} found", state), - } - } } impl<'b> ComponentWidgetsMut<'b> { @@ -90,17 +58,33 @@ impl<'b> ComponentWidgetsMut<'b> { return; }; - let widget_id = match content { - RootWidgetMembers::Detail(widget) => widget.__id__, - RootWidgetMembers::Form(widget) => widget.__id__, - RootWidgetMembers::Inline(widget) => widget.__id__, - RootWidgetMembers::List(widget) => widget.__id__, - RootWidgetMembers::Grid(widget) => widget.__id__, - }; + match content { + RootWidgetMembers::Detail(widget) => { + let state = self.state.root_state_mut(widget.__id__); - let state = self.root_state_mut(widget_id); + state.show_action_panel = !state.show_action_panel; + } + RootWidgetMembers::Form(widget) => { + let state = self.state.root_state_mut(widget.__id__); - state.show_action_panel = !state.show_action_panel; + state.show_action_panel = !state.show_action_panel; + } + RootWidgetMembers::Inline(widget) => { + let state = self.state.root_state_mut(widget.__id__); + + state.show_action_panel = !state.show_action_panel; + } + RootWidgetMembers::List(widget) => { + let state = self.state.scrollable_root_state_mut(widget.__id__); + + state.show_action_panel = !state.show_action_panel; + } + RootWidgetMembers::Grid(widget) => { + let state = self.state.scrollable_root_state_mut(widget.__id__); + + state.show_action_panel = !state.show_action_panel; + } + } } pub fn append_text(&mut self, text: &str) -> Task { @@ -128,15 +112,12 @@ impl<'b> ComponentWidgetsMut<'b> { _ => return Task::none(), }; - let TextFieldState { - text_input_id, - state_value, - } = ComponentWidgetsMut::text_field_state_mut_on_state(&mut self.state, widget_id); + let state = self.state.text_field_state_mut(widget_id); if let Some(value) = text.chars().next().filter(|c| !c.is_control()) { - *state_value = format!("{}{}", state_value, value); + state.state_value = format!("{}{}", state.state_value, value); - text_input::focus(text_input_id.clone()) + text_input::focus(state.text_input_id.clone()) } else { Task::none() } @@ -167,16 +148,13 @@ impl<'b> ComponentWidgetsMut<'b> { _ => return Task::none(), }; - let TextFieldState { - text_input_id, - state_value, - } = ComponentWidgetsMut::text_field_state_mut_on_state(&mut self.state, widget_id); + let state = self.state.text_field_state_mut(widget_id); - let mut chars = state_value.chars(); + let mut chars = state.state_value.chars(); chars.next_back(); - *state_value = chars.as_str().to_owned(); + state.state_value = chars.as_str().to_owned(); - text_input::focus(text_input_id.clone()) + text_input::focus(state.text_input_id.clone()) } pub fn focus_up(&mut self) -> Task { @@ -192,41 +170,39 @@ impl<'b> ComponentWidgetsMut<'b> { RootWidgetMembers::Detail(_) => Task::none(), RootWidgetMembers::Form(_) => Task::none(), RootWidgetMembers::Inline(_) => Task::none(), - RootWidgetMembers::List(list_widget) => { - let RootState { focused_item, .. } = - ComponentWidgetsMut::root_state_mut_on_field(&mut self.state, list_widget.__id__); - - let focus_task = focused_item.focus_previous().unwrap_or_else(|| Task::none()); - - let item_focus_event = - ComponentWidgets::list_item_focus_event(self.plugin_id.clone(), focused_item, list_widget); - - Task::batch([item_focus_event, focus_task]) - } - RootWidgetMembers::Grid(grid_widget) => { - let RootState { focused_item, .. } = - ComponentWidgetsMut::root_state_mut_on_field(&mut self.state, grid_widget.__id__); - - let Some(current_index) = &focused_item.index else { + RootWidgetMembers::List(widget) => { + let ids = self.list_collect_ids(&widget); + let state = self.state.scrollable_root_state_mut(widget.__id__); + let (next_item, Some(focus_task)) = state.scroll_handle.list_focus_up(ids) else { return Task::none(); }; - let amount_per_section_total = ComponentWidgets::grid_section_sizes(grid_widget); + let state = self.state.scrollable_root_state(widget.__id__); + let focus_event = ComponentWidgets::from_mut(&self).list_item_focus_event( + self.plugin_id.clone(), + &state.scroll_handle, + next_item, + widget, + ); - let focus_task = match grid_up_offset(*current_index, amount_per_section_total) { - None => Task::none(), - Some(data) => { - match focused_item.focus_previous_in(data.offset) { - None => Task::none(), - Some(_) => focused_item.scroll_to(data.row_index), - } - } + Task::batch([focus_event, focus_task]) + } + RootWidgetMembers::Grid(widget) => { + let ids = self.grid_collect_ids(widget); + let state = self.state.scrollable_root_state_mut(widget.__id__); + let (next_item, Some(focus_task)) = state.scroll_handle.grid_focus_up(ids) else { + return Task::none(); }; - let item_focus_event = - ComponentWidgets::grid_item_focus_event(self.plugin_id.clone(), focused_item, grid_widget); + let state = self.state.scrollable_root_state(widget.__id__); + let focus_event = ComponentWidgets::from_mut(&self).grid_item_focus_event( + self.plugin_id.clone(), + &state.scroll_handle, + next_item, + widget, + ); - Task::batch([item_focus_event, focus_task]) + Task::batch([focus_event, focus_task]) } } } @@ -243,81 +219,46 @@ impl<'b> ComponentWidgetsMut<'b> { match content { RootWidgetMembers::Detail(_) => Task::none(), RootWidgetMembers::Form(_) => Task::none(), - RootWidgetMembers::Inline(_) => Task::none(), RootWidgetMembers::List(widget) => { - let RootState { focused_item, .. } = - ComponentWidgetsMut::root_state_mut_on_field(&mut self.state, widget.__id__); - - let total = widget - .content - .ordered_members - .iter() - .flat_map(|members| { - match members { - ListWidgetOrderedMembers::ListItem(widget) => vec![widget], - ListWidgetOrderedMembers::ListSection(widget) => { - widget - .content - .ordered_members - .iter() - .map(|members| { - match members { - ListSectionWidgetOrderedMembers::ListItem(widget) => widget, - } - }) - .collect() - } - } - }) - .count(); - - let focus_task = focused_item.focus_next(total).unwrap_or_else(|| Task::none()); - - let item_focus_event = - ComponentWidgets::list_item_focus_event(self.plugin_id.clone(), focused_item, widget); - - Task::batch([item_focus_event, focus_task]) - } - RootWidgetMembers::Grid(grid_widget) => { - let RootState { focused_item, .. } = - ComponentWidgetsMut::root_state_mut_on_field(&mut self.state, grid_widget.__id__); - - let amount_per_section_total = ComponentWidgets::grid_section_sizes(grid_widget); - - let total = amount_per_section_total.iter().map(|data| data.amount_in_section).sum(); - - let Some(current_index) = &focused_item.index else { - let unfocus = match &grid_widget.content.search_bar { - None => Task::none(), - Some(_) => { - // there doesn't seem to be an unfocus command but focusing non-existing input will unfocus all - text_input::focus(text_input::Id::unique()) - } - }; - - let _ = focused_item.focus_next(total); - - let item_focus_event = - ComponentWidgets::grid_item_focus_event(self.plugin_id.clone(), focused_item, grid_widget); - - return Task::batch([unfocus, focused_item.scroll_to(0), item_focus_event]); + let ids = self.list_collect_ids(widget); + let state = self.state.scrollable_root_state_mut(widget.__id__); + let (next_item, Some(focus_task)) = state.scroll_handle.list_focus_down(ids) else { + return Task::none(); }; - let focus_task = match grid_down_offset(*current_index, amount_per_section_total) { + let state = self.state.scrollable_root_state(widget.__id__); + let focus_event = ComponentWidgets::from_mut(&self).list_item_focus_event( + self.plugin_id.clone(), + &state.scroll_handle, + next_item, + widget, + ); + + Task::batch([focus_event, focus_task]) + } + RootWidgetMembers::Grid(widget) => { + let unfocus_search_bar = match &widget.content.search_bar { + Some(_) => operate(unfocus()), None => Task::none(), - Some(data) => { - match focused_item.focus_next_in(total, data.offset) { - None => Task::none(), - Some(_) => focused_item.scroll_to(data.row_index), - } - } }; - let item_focus_event = - ComponentWidgets::grid_item_focus_event(self.plugin_id.clone(), focused_item, grid_widget); + let ids = self.grid_collect_ids(widget); + let state = self.state.scrollable_root_state_mut(widget.__id__); + let (next_item, Some(focus_task)) = state.scroll_handle.grid_focus_down(ids) else { + return Task::none(); + }; - Task::batch([item_focus_event, focus_task]) + let state = self.state.scrollable_root_state(widget.__id__); + let focus_event = ComponentWidgets::from_mut(&self).grid_item_focus_event( + self.plugin_id.clone(), + &state.scroll_handle, + next_item, + widget, + ); + + Task::batch([unfocus_search_bar, focus_task, focus_event]) } + RootWidgetMembers::Inline(_) => Task::none(), } } @@ -335,15 +276,22 @@ impl<'b> ComponentWidgetsMut<'b> { RootWidgetMembers::Form(_) => Task::none(), RootWidgetMembers::Inline(_) => Task::none(), RootWidgetMembers::List(_) => Task::none(), - RootWidgetMembers::Grid(grid_widget) => { - let RootState { focused_item, .. } = - ComponentWidgetsMut::root_state_mut_on_field(&mut self.state, grid_widget.__id__); + RootWidgetMembers::Grid(widget) => { + let ids = self.grid_collect_ids(widget); + let state = self.state.scrollable_root_state_mut(widget.__id__); + let (next_item, Some(focus_task)) = state.scroll_handle.grid_focus_left(ids) else { + return Task::none(); + }; - let _ = focused_item.focus_previous(); + let state = self.state.scrollable_root_state(widget.__id__); + let focus_event = ComponentWidgets::from_mut(&self).grid_item_focus_event( + self.plugin_id.clone(), + &state.scroll_handle, + next_item, + widget, + ); - // focused_item.scroll_to(0) - - ComponentWidgets::grid_item_focus_event(self.plugin_id.clone(), focused_item, grid_widget) + Task::batch([focus_event, focus_task]) } } } @@ -362,39 +310,137 @@ impl<'b> ComponentWidgetsMut<'b> { RootWidgetMembers::Form(_) => Task::none(), RootWidgetMembers::Inline(_) => Task::none(), RootWidgetMembers::List(_) => Task::none(), - RootWidgetMembers::Grid(grid_widget) => { - let RootState { focused_item, .. } = - ComponentWidgetsMut::root_state_mut_on_field(&mut self.state, grid_widget.__id__); + RootWidgetMembers::Grid(widget) => { + let ids = self.grid_collect_ids(widget); + let state = self.state.scrollable_root_state_mut(widget.__id__); + let (next_item, Some(focus_task)) = state.scroll_handle.grid_focus_right(ids) else { + return Task::none(); + }; - let total = grid_widget - .content - .ordered_members - .iter() - .flat_map(|members| { - match members { - GridWidgetOrderedMembers::GridItem(widget) => vec![widget], - GridWidgetOrderedMembers::GridSection(widget) => { - widget - .content - .ordered_members - .iter() - .map(|members| { - match members { - GridSectionWidgetOrderedMembers::GridItem(widget) => widget, - } - }) - .collect() - } - } - }) - .count(); + let state = self.state.scrollable_root_state(widget.__id__); + let focus_event = ComponentWidgets::from_mut(&self).grid_item_focus_event( + self.plugin_id.clone(), + &state.scroll_handle, + next_item, + widget, + ); - let _ = focused_item.focus_next(total); - - // focused_item.scroll_to(0) - - ComponentWidgets::grid_item_focus_event(self.plugin_id.clone(), focused_item, grid_widget) + Task::batch([focus_event, focus_task]) } } } + + pub fn set_focused_item_id(&mut self, target_id: Option) -> Task { + let Some(root) = self.root_widget else { + return Task::none(); + }; + + let Some(content) = &root.content else { + return Task::none(); + }; + + match content { + RootWidgetMembers::List(widget) => { + let state = self.state.scrollable_root_state_mut(widget.__id__); + + state.scroll_handle.set_current_focused_item(target_id) + } + RootWidgetMembers::Grid(widget) => { + let state = self.state.scrollable_root_state_mut(widget.__id__); + + state.scroll_handle.set_current_focused_item(target_id) + } + _ => Task::none(), + } + } + + fn list_collect_ids(&self, list_widget: &ListWidget) -> Vec { + list_widget + .content + .ordered_members + .iter() + .flat_map(|members| { + match members { + ListWidgetOrderedMembers::ListItem(widget) => { + let state = self.state.scrollable_item_state(widget.__id__); + + vec![state.id.clone()] + } + ListWidgetOrderedMembers::ListSection(widget) => { + widget + .content + .ordered_members + .iter() + .map(|members| { + match members { + ListSectionWidgetOrderedMembers::ListItem(widget) => { + let state = self.state.scrollable_item_state(widget.__id__); + state.id.clone() + } + } + }) + .collect() + } + } + }) + .collect() + } + + fn grid_collect_ids(&self, grid_widget: &GridWidget) -> Vec> { + let global_columns = grid_width(&grid_widget.columns); + + fn create_section(items: Vec, columns: usize) -> Vec> { + items + .into_iter() + .chunks(columns) + .into_iter() + .map(|iter| iter.collect()) + .collect() + } + + let mut result = vec![]; + let mut pending = vec![]; + + for members in &grid_widget.content.ordered_members { + match members { + GridWidgetOrderedMembers::GridItem(widget) => { + let state = self.state.scrollable_item_state(widget.__id__); + + pending.push(state.id.clone()) + } + GridWidgetOrderedMembers::GridSection(widget) => { + if !pending.is_empty() { + let pending = mem::replace(&mut pending, vec![]); + result.extend(create_section(pending, global_columns)) + } + + let section_columns = grid_width(&widget.columns); + + let section = widget + .content + .ordered_members + .iter() + .map(|members| { + match members { + GridSectionWidgetOrderedMembers::GridItem(widget) => { + let state = self.state.scrollable_item_state(widget.__id__); + + state.id.clone() + } + } + }) + .collect(); + + result.extend(create_section(section, section_columns)) + } + } + } + + if !pending.is_empty() { + let pending = mem::replace(&mut pending, vec![]); + result.extend(create_section(pending, global_columns)) + } + + result + } } diff --git a/rust/client/src/ui/widget/form.rs b/rust/client/src/ui/widget/form.rs index 3b934db..16c0a67 100644 --- a/rust/client/src/ui/widget/form.rs +++ b/rust/client/src/ui/widget/form.rs @@ -42,7 +42,7 @@ use crate::ui::widget::state::TextFieldState; impl<'b> ComponentWidgets<'b> { fn render_text_field_widget<'a>(&self, widget: &TextFieldWidget) -> Element<'a, ComponentWidgetEvent> { let widget_id = widget.__id__; - let TextFieldState { state_value, .. } = self.text_field_state(widget.__id__); + let TextFieldState { state_value, .. } = self.state.text_field_state(widget.__id__); text_input("", state_value) .on_input(move |value| ComponentWidgetEvent::OnChangeTextField { widget_id, value }) @@ -51,7 +51,7 @@ impl<'b> ComponentWidgets<'b> { fn render_password_field_widget<'a>(&self, widget: &PasswordFieldWidget) -> Element<'a, ComponentWidgetEvent> { let widget_id = widget.__id__; - let TextFieldState { state_value, .. } = self.text_field_state(widget_id); + let TextFieldState { state_value, .. } = self.state.text_field_state(widget_id); text_input("", state_value) .secure(true) @@ -61,7 +61,7 @@ impl<'b> ComponentWidgets<'b> { fn render_checkbox_widget<'a>(&self, widget: &CheckboxWidget) -> Element<'a, ComponentWidgetEvent> { let widget_id = widget.__id__; - let CheckboxState { state_value } = self.checkbox_state(widget_id); + let CheckboxState { state_value } = self.state.checkbox_state(widget_id); checkbox(widget.title.as_deref().unwrap_or_default(), state_value.to_owned()) .on_toggle(move |value| ComponentWidgetEvent::ToggleCheckbox { widget_id, value }) @@ -70,7 +70,7 @@ impl<'b> ComponentWidgets<'b> { fn render_select_widget<'a>(&self, widget: &SelectWidget) -> Element<'a, ComponentWidgetEvent> { let widget_id = widget.__id__; - let SelectState { state_value } = self.select_state(widget_id); + let SelectState { state_value } = self.state.select_state(widget_id); let items: Vec<_> = widget .content @@ -115,7 +115,7 @@ impl<'b> ComponentWidgets<'b> { action_shortcuts: &HashMap, ) -> Element<'a, ComponentWidgetEvent> { let widget_id = widget.__id__; - let RootState { show_action_panel, .. } = self.root_state(widget_id); + let RootState { show_action_panel, .. } = self.state.root_state(widget_id); let items: Vec> = widget .content diff --git a/rust/client/src/ui/widget/grid.rs b/rust/client/src/ui/widget/grid.rs index c12c8c2..1201d30 100644 --- a/rust/client/src/ui/widget/grid.rs +++ b/rust/client/src/ui/widget/grid.rs @@ -30,7 +30,7 @@ use crate::ui::theme::text::TextStyle; use crate::ui::widget::accessories::render_icon_accessory; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; -use crate::ui::widget::state::RootState; +use crate::ui::widget::state::ScrollableRootState; impl<'b> ComponentWidgets<'b> { pub fn render_grid_widget<'a>( @@ -40,10 +40,10 @@ impl<'b> ComponentWidgets<'b> { entrypoint_name: &str, action_shortcuts: &HashMap, ) -> Element<'a, ComponentWidgetEvent> { - let RootState { + let ScrollableRootState { show_action_panel, - focused_item, - } = self.root_state(grid_widget.__id__); + scroll_handle, + } = self.state.scrollable_root_state(grid_widget.__id__); let content = if grid_widget.content.ordered_members.is_empty() { match &grid_widget.content.empty_view { @@ -67,7 +67,7 @@ impl<'b> ComponentWidgets<'b> { let content = self.render_grid_section( &pending, &grid_widget.columns, - focused_item.index, + scroll_handle.current_item_id.clone(), index_counter, ); @@ -78,7 +78,7 @@ impl<'b> ComponentWidgets<'b> { items.push(self.render_grid_section_widget( widget, - focused_item.index, + scroll_handle.current_item_id.clone(), index_counter, first_section, )); @@ -89,8 +89,12 @@ impl<'b> ComponentWidgets<'b> { } if !pending.is_empty() { - let content = - self.render_grid_section(&pending, &grid_widget.columns, focused_item.index, index_counter); + let content = self.render_grid_section( + &pending, + &grid_widget.columns, + scroll_handle.current_item_id.clone(), + index_counter, + ); items.push(content); } @@ -100,7 +104,7 @@ impl<'b> ComponentWidgets<'b> { let content: Element<_> = container(content).width(Length::Fill).themed(ContainerStyle::GridInner); let content: Element<_> = scrollable(content) - .id(focused_item.scrollable_id.clone()) + .id(scroll_handle.scrollable_id.clone()) .width(Length::Fill) .into(); @@ -109,7 +113,8 @@ impl<'b> ComponentWidgets<'b> { content }; - let focused_item_id = ComponentWidgets::grid_focused_item_id(focused_item, grid_widget); + let focused_item_id = + self.grid_focused_item_id(scroll_handle, scroll_handle.current_item_id.clone(), grid_widget); self.render_plugin_root( *show_action_panel, @@ -128,7 +133,7 @@ impl<'b> ComponentWidgets<'b> { fn render_grid_section_widget<'a>( &self, widget: &GridSectionWidget, - item_focus_index: Option, + item_focused_id: Option, index_counter: &Cell, first_section: bool, ) -> Element<'a, ComponentWidgetEvent> { @@ -143,7 +148,7 @@ impl<'b> ComponentWidgets<'b> { }) .collect(); - let content = self.render_grid_section(&items, &widget.columns, item_focus_index, index_counter); + let content = self.render_grid_section(&items, &widget.columns, item_focused_id, index_counter); let section_title_style = if first_section { RowStyle::GridFirstSectionTitle @@ -164,7 +169,7 @@ impl<'b> ComponentWidgets<'b> { fn render_grid_item_widget<'a>( &self, widget: &GridItemWidget, - item_focus_index: Option, + item_focused_id: Option, index_counter: &Cell, grid_width: usize, ) -> Element<'a, ComponentWidgetEvent> { @@ -178,14 +183,16 @@ impl<'b> ComponentWidgets<'b> { 8.. => 50, }; + let state = self.state.scrollable_item_state(widget.__id__); + let content: Element<_> = container(self.render_content_widget(&widget.content.content, true)) .height(height) .into(); - let style = match item_focus_index { + let style = match &item_focused_id { None => ButtonStyle::GridItem, Some(focused_index) => { - if focused_index == index_counter.get() { + if focused_index == &state.id { ButtonStyle::GridItemFocused } else { ButtonStyle::GridItem @@ -195,7 +202,7 @@ impl<'b> ComponentWidgets<'b> { index_counter.set(index_counter.get() + 1); - let action_ids = self.get_action_ids(); + let action_ids = self.get_action_widgets(); let primary_action = action_ids.first(); let on_press_msg = match primary_action { @@ -242,6 +249,8 @@ impl<'b> ComponentWidgets<'b> { let content: Element<_> = column(vec![content, sub_content]).width(Length::Fill).into(); + let content: Element<_> = container(content).id(state.id.clone()).into(); + content } @@ -250,7 +259,7 @@ impl<'b> ComponentWidgets<'b> { items: &[&GridItemWidget], /*aspect_ratio: Option<&str>,*/ columns: &Option, - item_focus_index: Option, + item_focused_id: Option, index_counter: &Cell, ) -> Element<'a, ComponentWidgetEvent> { // TODO @@ -270,7 +279,7 @@ impl<'b> ComponentWidgets<'b> { let rows: Vec> = items .iter() - .map(|widget| self.render_grid_item_widget(widget, item_focus_index, index_counter, columns)) + .map(|widget| self.render_grid_item_widget(widget, item_focused_id.clone(), index_counter, columns)) .chunks(columns) .into_iter() .flat_map(|row_items| row_items) diff --git a/rust/client/src/ui/widget/list.rs b/rust/client/src/ui/widget/list.rs index 5769067..759b337 100644 --- a/rust/client/src/ui/widget/list.rs +++ b/rust/client/src/ui/widget/list.rs @@ -33,7 +33,7 @@ use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::grid::render_section; use crate::ui::widget::images::render_image; -use crate::ui::widget::state::RootState; +use crate::ui::widget::state::ScrollableRootState; impl<'b> ComponentWidgets<'b> { pub fn render_list_widget<'a>( @@ -44,10 +44,10 @@ impl<'b> ComponentWidgets<'b> { action_shortcuts: &HashMap, ) -> Element<'a, ComponentWidgetEvent> { let widget_id = list_widget.__id__; - let RootState { + let ScrollableRootState { show_action_panel, - focused_item, - } = self.root_state(widget_id); + scroll_handle, + } = self.state.scrollable_root_state(widget_id); let mut pending: Vec<&ListItemWidget> = vec![]; let mut items: Vec> = vec![]; @@ -64,7 +64,13 @@ impl<'b> ComponentWidgets<'b> { if !pending.is_empty() { let content: Vec<_> = pending .iter() - .map(|widget| self.render_list_item_widget(widget, focused_item.index, index_counter)) + .map(|widget| { + self.render_list_item_widget( + widget, + scroll_handle.current_item_id.clone(), + index_counter, + ) + }) .collect(); let content: Element<_> = column(content).into(); @@ -76,7 +82,7 @@ impl<'b> ComponentWidgets<'b> { items.push(self.render_list_section_widget( widget, - focused_item.index, + scroll_handle.current_item_id.clone(), index_counter, first_section, )); @@ -89,7 +95,9 @@ impl<'b> ComponentWidgets<'b> { if !pending.is_empty() { let content: Vec<_> = pending .iter() - .map(|widget| self.render_list_item_widget(widget, focused_item.index, index_counter)) + .map(|widget| { + self.render_list_item_widget(widget, scroll_handle.current_item_id.clone(), index_counter) + }) .collect(); let content: Element<_> = column(content).into(); @@ -108,7 +116,7 @@ impl<'b> ComponentWidgets<'b> { let content: Element<_> = container(content).width(Length::Fill).themed(ContainerStyle::ListInner); let content: Element<_> = scrollable(content) - .id(focused_item.scrollable_id.clone()) + .id(scroll_handle.scrollable_id.clone()) .width(Length::Fill) .into(); @@ -135,7 +143,8 @@ impl<'b> ComponentWidgets<'b> { let content: Element<_> = row(elements).height(Length::Fill).into(); - let focused_item_id = ComponentWidgets::list_focused_item_id(focused_item, list_widget); + let focused_item_id = + self.list_focused_item_id(scroll_handle, scroll_handle.current_item_id.clone(), list_widget); self.render_plugin_root( *show_action_panel, @@ -154,7 +163,7 @@ impl<'b> ComponentWidgets<'b> { fn render_list_section_widget<'a>( &self, widget: &ListSectionWidget, - item_focus_index: Option, + item_focused_id: Option, index_counter: &Cell, first_section: bool, ) -> Element<'a, ComponentWidgetEvent> { @@ -165,7 +174,7 @@ impl<'b> ComponentWidgets<'b> { .map(|members| { match members { ListSectionWidgetOrderedMembers::ListItem(widget) => { - self.render_list_item_widget(widget, item_focus_index, index_counter) + self.render_list_item_widget(widget, item_focused_id.clone(), index_counter) } } }) @@ -192,7 +201,7 @@ impl<'b> ComponentWidgets<'b> { fn render_list_item_widget<'a>( &self, widget: &ListItemWidget, - item_focus_index: Option, + item_focused_id: Option, index_counter: &Cell, ) -> Element<'a, ComponentWidgetEvent> { let icon: Option> = widget @@ -243,10 +252,12 @@ impl<'b> ComponentWidgets<'b> { let content: Element<_> = row(content).align_y(Alignment::Center).into(); - let style = match item_focus_index { + let state = self.state.scrollable_item_state(widget.__id__); + + let style = match &item_focused_id { None => ButtonStyle::ListItem, Some(focused_index) => { - if focused_index == index_counter.get() { + if focused_index == &state.id { ButtonStyle::ListItemFocused } else { ButtonStyle::ListItem @@ -256,7 +267,7 @@ impl<'b> ComponentWidgets<'b> { index_counter.set(index_counter.get() + 1); - let action_ids = self.get_action_ids(); + let action_ids = self.get_action_widgets(); let primary_action = action_ids.first(); let on_press_msg = match primary_action { @@ -269,6 +280,8 @@ impl<'b> ComponentWidgets<'b> { } }; - button(content).on_press(on_press_msg).width(Length::Fill).themed(style) + let element = button(content).on_press(on_press_msg).width(Length::Fill).themed(style); + + container(element).id(state.id.clone()).into() } } diff --git a/rust/client/src/ui/widget/root.rs b/rust/client/src/ui/widget/root.rs index c215047..8b7b5be 100644 --- a/rust/client/src/ui/widget/root.rs +++ b/rust/client/src/ui/widget/root.rs @@ -23,6 +23,7 @@ use iced::widget::vertical_rule; use iced_fonts::bootstrap::arrow_left; use crate::ui::custom_widgets::loading_bar::LoadingBar; +use crate::ui::primary_shortcut; use crate::ui::scroll_handle::ScrollHandle; use crate::ui::state::PluginViewState; use crate::ui::theme::Element; @@ -57,7 +58,7 @@ impl<'b> ComponentWidgets<'b> { match content { RootWidgetMembers::Detail(widget) => { - let RootState { show_action_panel, .. } = self.root_state(widget.__id__); + let RootState { show_action_panel, .. } = self.state.root_state(widget.__id__); let content = self.render_detail_widget(widget, false); @@ -167,22 +168,11 @@ impl<'b> ComponentWidgets<'b> { let mut action_panel = convert_action_panel(action_panel, &action_shortcuts); - let primary_action = - action_panel - .as_mut() - .map(|panel| panel.find_first()) - .flatten() - .map(|(label, widget_id)| { - let shortcut = PhysicalShortcut { - physical_key: PhysicalKey::Enter, - modifier_shift: false, - modifier_control: false, - modifier_alt: false, - modifier_meta: false, - }; - - (label.to_string(), widget_id, shortcut) - }); + let primary_action = action_panel + .as_mut() + .map(|panel| panel.find_first()) + .flatten() + .map(|(label, widget_id)| (label.to_string(), widget_id, primary_shortcut())); match plugin_view_state { PluginViewState::None => { @@ -216,7 +206,7 @@ impl<'b> ComponentWidgets<'b> { || ComponentWidgetEvent::Noop, ) } - PluginViewState::ActionPanel { focused_action_item } => { + PluginViewState::ActionPanel { scroll_handle } => { render_root( show_action_panel, top_panel, @@ -225,7 +215,7 @@ impl<'b> ComponentWidgets<'b> { content, primary_action, action_panel, - Some(&focused_action_item), + Some(&scroll_handle), entrypoint_name, || { ComponentWidgetEvent::ToggleActionPanel { diff --git a/rust/client/src/ui/widget/search_bar.rs b/rust/client/src/ui/widget/search_bar.rs index a133777..42533a8 100644 --- a/rust/client/src/ui/widget/search_bar.rs +++ b/rust/client/src/ui/widget/search_bar.rs @@ -14,7 +14,7 @@ impl<'b> ComponentWidgets<'b> { let TextFieldState { state_value, text_input_id, - } = self.text_field_state(widget_id); + } = self.state.text_field_state(widget_id); text_input(widget.placeholder.as_deref().unwrap_or_default(), state_value) .id(text_input_id.clone()) diff --git a/rust/client/src/ui/widget/state.rs b/rust/client/src/ui/widget/state.rs index dd1cf27..b0a971d 100644 --- a/rust/client/src/ui/widget/state.rs +++ b/rust/client/src/ui/widget/state.rs @@ -3,16 +3,17 @@ use std::collections::HashMap; use gauntlet_common::model::FormWidgetOrderedMembers; use gauntlet_common::model::GridSectionWidgetOrderedMembers; use gauntlet_common::model::GridWidgetOrderedMembers; +use gauntlet_common::model::ListSectionWidgetOrderedMembers; +use gauntlet_common::model::ListWidgetOrderedMembers; use gauntlet_common::model::RootWidget; use gauntlet_common::model::RootWidgetMembers; use gauntlet_common::model::UiWidgetId; +use iced::widget::container; use iced::widget::text_input; -use crate::ui::scroll_handle::ESTIMATED_MAIN_LIST_ITEM_HEIGHT; use crate::ui::scroll_handle::ScrollHandle; -use crate::ui::widget::grid::grid_width; -pub fn create_state(root_widget: &RootWidget) -> HashMap { +pub fn create_state(root_widget: &RootWidget) -> ComponentWidgetStateContainer { let mut result = HashMap::new(); match &root_widget.content { @@ -20,11 +21,9 @@ pub fn create_state(root_widget: &RootWidget) -> HashMap { match members { RootWidgetMembers::Detail(widget) => { - result.insert(widget.__id__, ComponentWidgetState::root(0.0, 0)); + result.insert(widget.__id__, ComponentWidgetState::root()); } RootWidgetMembers::Form(widget) => { - result.insert(widget.__id__, ComponentWidgetState::root(0.0, 0)); - for members in &widget.content.ordered_members { match members { FormWidgetOrderedMembers::TextField(widget) => { @@ -42,74 +41,73 @@ pub fn create_state(root_widget: &RootWidget) -> HashMap {} } } + + result.insert(widget.__id__, ComponentWidgetState::root()); } RootWidgetMembers::List(widget) => { - result.insert( - widget.__id__, - ComponentWidgetState::root(ESTIMATED_MAIN_LIST_ITEM_HEIGHT, 7), - ); - if let Some(widget) = &widget.content.search_bar { result.insert(widget.__id__, ComponentWidgetState::text_field(&widget.value)); } - } - RootWidgetMembers::Grid(widget) => { - // cursed heuristic - let has_title = widget - .content - .ordered_members - .iter() - .flat_map(|members| { - match members { - GridWidgetOrderedMembers::GridItem(widget) => vec![widget], - GridWidgetOrderedMembers::GridSection(widget) => { - widget - .content - .ordered_members - .iter() - .map(|members| { - match members { - GridSectionWidgetOrderedMembers::GridItem(widget) => widget, - } - }) - .collect() + + for members in &widget.content.ordered_members { + match members { + ListWidgetOrderedMembers::ListItem(widget) => { + result.insert(widget.__id__, ComponentWidgetState::scrollable_item()); + } + ListWidgetOrderedMembers::ListSection(widget) => { + for members in &widget.content.ordered_members { + match members { + ListSectionWidgetOrderedMembers::ListItem(widget) => { + result.insert(widget.__id__, ComponentWidgetState::scrollable_item()); + } + } } } - }) - .next() - .map(|widget| widget.title.is_some() || widget.subtitle.is_some()) - .unwrap_or_default(); - - let (height, rows_per_view) = match grid_width(&widget.columns) { - ..4 => (150.0, 0), - 4 => (150.0, 0), - 5 => (130.0, 0), - 6 => (110.0, 1), - 7 => (90.0, 3), - 8 => (if has_title { 50.0 } else { 50.0 }, if has_title { 3 } else { 4 }), - 8.. => (50.0, 4), - }; - - result.insert(widget.__id__, ComponentWidgetState::root(height, rows_per_view)); + } + } + result.insert(widget.__id__, ComponentWidgetState::scrollable_root()); + } + RootWidgetMembers::Grid(widget) => { if let Some(widget) = &widget.content.search_bar { result.insert(widget.__id__, ComponentWidgetState::text_field(&widget.value)); } + + for members in &widget.content.ordered_members { + match members { + GridWidgetOrderedMembers::GridItem(widget) => { + result.insert(widget.__id__, ComponentWidgetState::scrollable_item()); + } + GridWidgetOrderedMembers::GridSection(widget) => { + for members in &widget.content.ordered_members { + match members { + GridSectionWidgetOrderedMembers::GridItem(widget) => { + result.insert(widget.__id__, ComponentWidgetState::scrollable_item()); + } + } + } + } + } + } + + result.insert(widget.__id__, ComponentWidgetState::scrollable_root()); } RootWidgetMembers::Inline(_) => {} } } } - result + ComponentWidgetStateContainer(result) } #[derive(Debug, Clone)] pub enum ComponentWidgetState { + ScrollableItem(ScrollableItemState), TextField(TextFieldState), Checkbox(CheckboxState), Select(SelectState), Root(RootState), + ScrollableRoot(ScrollableRootState), } #[derive(Debug, Clone)] @@ -131,14 +129,30 @@ pub struct SelectState { #[derive(Debug, Clone)] pub struct RootState { pub show_action_panel: bool, - pub focused_item: ScrollHandle, +} + +#[derive(Debug, Clone)] +pub struct ScrollableRootState { + pub show_action_panel: bool, + pub scroll_handle: ScrollHandle, +} + +#[derive(Debug, Clone)] +pub struct ScrollableItemState { + pub id: container::Id, } impl ComponentWidgetState { - fn root(item_height: f32, rows_per_view: usize) -> ComponentWidgetState { + fn root() -> ComponentWidgetState { ComponentWidgetState::Root(RootState { show_action_panel: false, - focused_item: ScrollHandle::new(false, item_height, rows_per_view), + }) + } + + fn scrollable_root() -> ComponentWidgetState { + ComponentWidgetState::ScrollableRoot(ScrollableRootState { + show_action_panel: false, + scroll_handle: ScrollHandle::new(None), }) } @@ -160,4 +174,138 @@ impl ComponentWidgetState { state_value: value.to_owned(), }) } + + fn scrollable_item() -> ComponentWidgetState { + ComponentWidgetState::ScrollableItem(ScrollableItemState { + id: container::Id::unique(), + }) + } +} + +#[derive(Debug)] +pub struct ComponentWidgetStateContainer(pub(crate) HashMap); + +impl ComponentWidgetStateContainer { + pub fn text_field_state(&self, widget_id: UiWidgetId) -> &TextFieldState { + let state = self.0.get(&widget_id).expect(&format!( + "requested state should always be present for id: {}", + widget_id + )); + + match state { + ComponentWidgetState::TextField(state) => state, + _ => panic!("TextFieldState expected, {:?} found", state), + } + } + + pub fn checkbox_state(&self, widget_id: UiWidgetId) -> &CheckboxState { + let state = self.0.get(&widget_id).expect(&format!( + "requested state should always be present for id: {}", + widget_id + )); + + match state { + ComponentWidgetState::Checkbox(state) => state, + _ => panic!("CheckboxState expected, {:?} found", state), + } + } + + pub fn select_state(&self, widget_id: UiWidgetId) -> &SelectState { + let state = self.0.get(&widget_id).expect(&format!( + "requested state should always be present for id: {}", + widget_id + )); + + match state { + ComponentWidgetState::Select(state) => state, + _ => panic!("SelectState expected, {:?} found", state), + } + } + + pub fn root_state(&self, widget_id: UiWidgetId) -> &RootState { + let state = self.0.get(&widget_id).expect(&format!( + "requested state should always be present for id: {}", + widget_id + )); + + match state { + ComponentWidgetState::Root(state) => state, + _ => panic!("RootState expected, {:?} found", state), + } + } + + pub fn scrollable_item_state(&self, widget_id: UiWidgetId) -> &ScrollableItemState { + let state = self.0.get(&widget_id).expect(&format!( + "requested state should always be present for id: {}", + widget_id + )); + + match state { + ComponentWidgetState::ScrollableItem(state) => state, + _ => panic!("ScrollableItem expected, {:?} found", state), + } + } + + pub fn scrollable_root_state(&self, widget_id: UiWidgetId) -> &ScrollableRootState { + let state = self.0.get(&widget_id).expect(&format!( + "requested state should always be present for id: {}", + widget_id + )); + + match state { + ComponentWidgetState::ScrollableRoot(state) => state, + _ => panic!("ScrollableRoot expected, {:?} found", state), + } + } +} + +impl ComponentWidgetStateContainer { + pub fn text_field_state_mut(&mut self, widget_id: UiWidgetId) -> &mut TextFieldState { + let state = self.0.get_mut(&widget_id).expect(&format!( + "requested state should always be present for id: {}", + widget_id + )); + + match state { + ComponentWidgetState::TextField(state) => state, + _ => panic!("TextFieldState expected, {:?} found", state), + } + } + + #[allow(unused)] + pub fn scrollable_item_state_mut(&mut self, widget_id: UiWidgetId) -> &mut ScrollableItemState { + let state = self.0.get_mut(&widget_id).expect(&format!( + "requested state should always be present for id: {}", + widget_id + )); + + match state { + ComponentWidgetState::ScrollableItem(state) => state, + _ => panic!("ScrollableItem expected, {:?} found", state), + } + } + + pub fn scrollable_root_state_mut(&mut self, widget_id: UiWidgetId) -> &mut ScrollableRootState { + let state = self.0.get_mut(&widget_id).expect(&format!( + "requested state should always be present for id: {}", + widget_id + )); + + match state { + ComponentWidgetState::ScrollableRoot(state) => state, + _ => panic!("ScrollableRoot expected, {:?} found", state), + } + } + + pub fn root_state_mut(&mut self, widget_id: UiWidgetId) -> &mut RootState { + let state = self.0.get_mut(&widget_id).expect(&format!( + "requested state should always be present for id: {}", + widget_id + )); + + match state { + ComponentWidgetState::Root(state) => state, + _ => panic!("RootState expected, {:?} found", state), + } + } } diff --git a/rust/client/src/ui/windows/mod.rs b/rust/client/src/ui/windows/mod.rs index bcae969..b5cc9fb 100644 --- a/rust/client/src/ui/windows/mod.rs +++ b/rust/client/src/ui/windows/mod.rs @@ -215,8 +215,6 @@ impl MainWindowState { commands.push(Task::done(AppMsg::ResetWindowState)); } - commands.push(Task::done(AppMsg::ResetMainWindowScroll)); - #[cfg(target_os = "macos")] macos_focus_previous_app(); @@ -234,7 +232,7 @@ impl MainWindowState { self.open_position, )); - Task::batch([ + let task = Task::batch([ open_task.map(|id| WindowActionMsg::SetMainWindowId(Some(id))), #[cfg(target_os = "macos")] match self.window_position_mode { @@ -243,8 +241,12 @@ impl MainWindowState { }, window::gain_focus(main_window_id), window::set_level(main_window_id, Level::AlwaysOnTop), + ]); + + Task::batch([ + task.map(AppMsg::WindowAction), + Task::done(AppMsg::ResetMainWindowItemFocus), ]) - .map(AppMsg::WindowAction) } } From 41f0fb460a2d698f7c449e97cba1719ab6e3776c Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 9 Aug 2025 19:23:29 +0200 Subject: [PATCH 76/91] Fix action execution from inline view not being executed after some refactor --- rust/client/src/ui/client_context.rs | 18 +++++++++++------- rust/client/src/ui/mod.rs | 4 ++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/rust/client/src/ui/client_context.rs b/rust/client/src/ui/client_context.rs index 28a0892..585bb62 100644 --- a/rust/client/src/ui/client_context.rs +++ b/rust/client/src/ui/client_context.rs @@ -30,7 +30,7 @@ impl ClientContext { } pub fn get_first_inline_view_container(&self) -> Option<&PluginViewContainer> { - self.get_all_inline_view_containers() + self.get_inline_view_containers() .iter() .next() .map(|(_, container)| container) @@ -47,14 +47,14 @@ impl ClientContext { .flatten() } - pub fn get_all_inline_view_containers(&self) -> Vec<&(PluginId, PluginViewContainer)> { + pub fn get_inline_view_containers(&self) -> Vec<&(PluginId, PluginViewContainer)> { self.views .iter() .filter(|(_, container)| matches!(container.render_location(), UiRenderLocation::InlineView)) .collect() } - pub fn get_mut_or_create_view_container( + pub fn get_mut_or_create_any_view_container( &mut self, render_location: UiRenderLocation, plugin_id: &PluginId, @@ -80,10 +80,14 @@ impl ClientContext { } pub fn get_mut_view_container(&mut self, plugin_id: &PluginId) -> Option<&mut PluginViewContainer> { + self.get_mut_any_view_container(plugin_id) + .filter(|container| matches!(container.render_location(), UiRenderLocation::View)) + } + + pub fn get_mut_any_view_container(&mut self, plugin_id: &PluginId) -> Option<&mut PluginViewContainer> { self.views .iter_mut() .find(|(id, _)| id == plugin_id) - .filter(|(_, container)| matches!(container.render_location(), UiRenderLocation::View)) .map(|(_, container)| container) } @@ -97,12 +101,12 @@ impl ClientContext { entrypoint_id: &EntrypointId, entrypoint_name: &str, ) -> AppMsg { - self.get_mut_or_create_view_container(render_location, plugin_id, entrypoint_id) + self.get_mut_or_create_any_view_container(render_location, plugin_id, entrypoint_id) .replace_view(container, data, plugin_name, entrypoint_name) } pub fn handle_event(&mut self, plugin_id: &PluginId, event: ComponentWidgetEvent) -> Option { - self.get_mut_view_container(plugin_id) + self.get_mut_any_view_container(plugin_id) .and_then(|view| view.handle_event(plugin_id.clone(), event)) } @@ -121,7 +125,7 @@ impl ClientContext { } pub fn set_current_focused_item(&mut self, plugin_id: PluginId, target_id: Option) -> Task { - self.get_mut_view_container(&plugin_id) + self.get_mut_any_view_container(&plugin_id) .map(|view| view.set_focused_item_id(target_id)) .unwrap_or(Task::none()) } diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 85d9330..5ba6fea 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -482,7 +482,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { state.application_manager.request_view_close(plugin_id); } - for (plugin_id, _) in state.client_context.get_all_inline_view_containers() { + for (plugin_id, _) in state.client_context.get_inline_view_containers() { state.application_manager.request_view_close(plugin_id.clone()); } state.client_context.clear_all_views(); @@ -1568,7 +1568,7 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { horizontal_rule(1).into() }; - let inline_view = match state.client_context.get_all_inline_view_containers().first() { + let inline_view = match state.client_context.get_inline_view_containers().first() { None => horizontal_space().into(), Some((plugin_id, container)) => { let plugin_id = plugin_id.clone(); From 62a16d04e07a7a515c95e49106ec08a76d188ea6 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 10 Aug 2025 10:50:29 +0200 Subject: [PATCH 77/91] Fix hud view being shown in a main window sometimes --- rust/client/src/ui/mod.rs | 75 +++++++++++++------------------ rust/client/src/ui/windows/hud.rs | 22 +++++++-- rust/client/src/ui/windows/mod.rs | 20 +++++++-- 3 files changed, 67 insertions(+), 50 deletions(-) diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index 5ba6fea..dd78eda 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::collections::HashSet; use std::sync::Arc; use std::sync::Mutex; @@ -131,8 +132,7 @@ pub struct AppModel { client_context: ClientContext, global_state: GlobalState, search_results: ScrollContent, - loading_bar_state: HashMap<(PluginId, EntrypointId), ()>, - hud_display: Option, + loading_bar_state: HashSet<(PluginId, EntrypointId)>, } #[derive(Debug, Clone)] @@ -281,9 +281,6 @@ pub enum AppMsg { WindowAction(WindowActionMsg), ResetWindowState, ResetMainWindowItemFocus, - SetHudDisplay { - display: String, - }, HandleScenario(ScenarioRunnerMsg), Settings(SettingsMsg), SetCurrentFocusedItem(Option), @@ -377,8 +374,7 @@ fn new(minimized: bool, #[allow(unused)] scenario_runner_data: Option String { match window { _ if Some(window) == state.main_window_state.main_window_id => "Gauntlet".to_owned(), _ if Some(window) == state.settings_window_state.settings_window_id => "Gauntlet Settings".to_owned(), - _ => "Gauntlet HUD".to_owned(), + _ if state.main_window_state.hud_windows.contains_key(&window) => "Gauntlet HUD".to_owned(), + _ => "".to_string(), } } @@ -1135,7 +1132,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { show, } => { if show { - state.loading_bar_state.insert((plugin_id, entrypoint_id), ()); + state.loading_bar_state.insert((plugin_id, entrypoint_id)); } else { state.loading_bar_state.remove(&(plugin_id, entrypoint_id)); } @@ -1244,11 +1241,6 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { _ => Task::none(), } } - AppMsg::SetHudDisplay { display } => { - state.hud_display = Some(display); - - Task::none() - } AppMsg::HandleScenario(msg) => { handle_scenario_runner_msg( msg, @@ -1307,42 +1299,39 @@ fn view(state: &AppModel, window: window::Id) -> Element<'_, AppMsg> { themer.map(AppMsg::Settings) } - _ => view_hud(state), + _ if state.main_window_state.hud_windows.contains_key(&window) => { + let display = state.main_window_state.hud_windows.get(&window).unwrap(); + + view_hud(display) + } + _ => horizontal_space().into(), } } -fn view_hud(state: &AppModel) -> Element<'_, AppMsg> { - match &state.hud_display { - Some(hud_display) => { - let hud: Element<_> = text(hud_display.to_string()).shaping(Shaping::Advanced).into(); +fn view_hud(hud_display: &str) -> Element<'_, AppMsg> { + let hud = text(hud_display.to_string()).shaping(Shaping::Advanced); - let hud = container(hud) - .align_x(Horizontal::Center) - .align_y(Vertical::Center) - .height(Length::Fill) - .themed(ContainerStyle::HudInner); + let hud = container(hud) + .align_x(Horizontal::Center) + .align_y(Vertical::Center) + .height(Length::Fill) + .themed(ContainerStyle::HudInner); - let hud = container(hud) - .align_x(Horizontal::Center) - .align_y(Vertical::Center) - .height(Length::Fill) - .themed(ContainerStyle::Hud); + let hud = container(hud) + .align_x(Horizontal::Center) + .align_y(Vertical::Center) + .height(Length::Fill) + .themed(ContainerStyle::Hud); - let hud = container(hud) - .height(Length::Fill) - .width(Length::Fill) - .align_x(Horizontal::Center) - .align_y(Vertical::Center) - .class(ContainerStyleInner::Transparent) - .into(); + let hud = container(hud) + .height(Length::Fill) + .width(Length::Fill) + .align_x(Horizontal::Center) + .align_y(Vertical::Center) + .class(ContainerStyleInner::Transparent) + .into(); - hud - } - None => { - // this should never be shown, but in case it does, do not make it fully transparent - container(horizontal_space()).themed(ContainerStyle::Hud) - } - } + hud } fn view_main(state: &AppModel) -> Element<'_, AppMsg> { diff --git a/rust/client/src/ui/windows/hud.rs b/rust/client/src/ui/windows/hud.rs index 6e9bc95..2ce3e9c 100644 --- a/rust/client/src/ui/windows/hud.rs +++ b/rust/client/src/ui/windows/hud.rs @@ -13,7 +13,7 @@ use crate::ui::windows::WindowActionMsg; const HUD_WINDOW_WIDTH: f32 = 400.0; const HUD_WINDOW_HEIGHT: f32 = 40.0; -pub fn show_hud_window(#[cfg(target_os = "linux")] layer_shell: bool) -> Task { +pub fn show_hud_window(#[cfg(target_os = "linux")] layer_shell: bool, display: String) -> Task { let settings = Settings { size: Size::new(HUD_WINDOW_WIDTH, HUD_WINDOW_HEIGHT), position: Position::SpecificWith(|window, screen| { @@ -45,9 +45,23 @@ pub fn show_hud_window(#[cfg(target_os = "linux")] layer_shell: bool) -> Task, open_position: Position, + pub hud_windows: HashMap, } impl MainWindowState { @@ -69,6 +71,7 @@ impl MainWindowState { #[cfg(target_os = "linux")] x11_active_window: None, open_position, + hud_windows: HashMap::new(), } } } @@ -105,10 +108,17 @@ impl MainWindowState { let show_hud = show_hud_window( #[cfg(target_os = "linux")] self.layer_shell, - ) - .map(AppMsg::WindowAction); + display, + ); - Task::batch([Task::done(AppMsg::SetHudDisplay { display }), show_hud]) + show_hud.map(AppMsg::WindowAction) + } + WindowActionMsg::SetHudWindowId { window_id, display } => { + match display { + None => self.hud_windows.remove(&window_id), + Some(display) => self.hud_windows.insert(window_id, display), + }; + Task::none() } WindowActionMsg::SetMainWindowId(id) => { self.main_window_id = id; @@ -267,6 +277,10 @@ pub enum WindowActionMsg { ShowHud { display: String, }, + SetHudWindowId { + window_id: window::Id, + display: Option, + }, } const WINDOW_WIDTH: f32 = 750.0; From 204e4182c903a7e197202d672df0d17e9e4abc26 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 10 Aug 2025 11:39:55 +0200 Subject: [PATCH 78/91] Make text smaller in a main view search results, plugin view, action panel and bottom panel right side --- rust/client/src/ui/search_list.rs | 9 +++++++-- rust/client/src/ui/widget/accessories.rs | 11 +++++++++-- rust/client/src/ui/widget/action_panel.rs | 5 +++-- rust/client/src/ui/widget/empty_view.rs | 1 + rust/client/src/ui/widget/form.rs | 1 + rust/client/src/ui/widget/grid.rs | 6 ++++-- rust/client/src/ui/widget/inline.rs | 1 + rust/client/src/ui/widget/list.rs | 6 +++++- rust/client/src/ui/widget/metadata.rs | 1 + rust/client/src/ui/widget/root.rs | 5 ++++- rust/client/src/ui/widget/text.rs | 2 +- 11 files changed, 37 insertions(+), 11 deletions(-) diff --git a/rust/client/src/ui/search_list.rs b/rust/client/src/ui/search_list.rs index 388cbf8..ee2263b 100644 --- a/rust/client/src/ui/search_list.rs +++ b/rust/client/src/ui/search_list.rs @@ -37,12 +37,16 @@ pub fn search_list<'a>( .items() .iter() .map(|(index, search_result)| { - let entrypoint_name: Element<_> = text(&search_result.entrypoint_name).shaping(Shaping::Advanced).into(); + let entrypoint_name: Element<_> = text(&search_result.entrypoint_name) + .size(15) + .shaping(Shaping::Advanced) + .into(); let entrypoint_name: Element<_> = container(entrypoint_name).themed(ContainerStyle::MainListItemText); let spacer: Element<_> = horizontal_space().width(Length::Fill).into(); let plugin_name_text: Element<_> = text(search_result.plugin_name.clone()) + .size(15) .shaping(Shaping::Advanced) .themed(TextStyle::MainListItemSubtext); @@ -70,7 +74,7 @@ pub fn search_list<'a>( button_content.push(plugin_name_text); if let Some(alias) = &search_result.entrypoint_alias { - let alias: Element<_> = text(alias.clone()).shaping(Shaping::Advanced).into(); + let alias: Element<_> = text(alias.clone()).shaping(Shaping::Advanced).size(15).into(); let alias: Element<_> = container(alias).themed(ContainerStyle::MainListItemAlias).into(); @@ -127,6 +131,7 @@ pub fn search_list<'a>( }; let type_text: Element<_> = text(type_text.to_string()) + .size(15) .shaping(Shaping::Advanced) .themed(TextStyle::MainListItemSubtext); diff --git a/rust/client/src/ui/widget/accessories.rs b/rust/client/src/ui/widget/accessories.rs index cccfce9..1a17566 100644 --- a/rust/client/src/ui/widget/accessories.rs +++ b/rust/client/src/ui/widget/accessories.rs @@ -34,7 +34,10 @@ pub fn render_icon_accessory<'a, T: 'a + Clone>( match widget.tooltip.as_ref() { None => content, Some(tooltip_text) => { - let tooltip_text: Element<_> = text(tooltip_text.to_string()).shaping(Shaping::Advanced).into(); + let tooltip_text: Element<_> = text(tooltip_text.to_string()) + .shaping(Shaping::Advanced) + .size(15) + .into(); tooltip(content, tooltip_text, Position::Top).themed(TooltipStyle::Tooltip) } @@ -52,6 +55,7 @@ pub fn render_text_accessory<'a, T: 'a + Clone>( let text_content: Element<_> = text(widget.text.to_string()) .shaping(Shaping::Advanced) + .size(15) .themed(TextStyle::TextAccessory); let mut content: Vec> = vec![]; @@ -74,7 +78,10 @@ pub fn render_text_accessory<'a, T: 'a + Clone>( match widget.tooltip.as_ref() { None => content, Some(tooltip_text) => { - let tooltip_text: Element<_> = text(tooltip_text.to_string()).shaping(Shaping::Advanced).into(); + let tooltip_text: Element<_> = text(tooltip_text.to_string()) + .shaping(Shaping::Advanced) + .size(15) + .into(); tooltip(content, tooltip_text, Position::Top).themed(TooltipStyle::Tooltip) } diff --git a/rust/client/src/ui/widget/action_panel.rs b/rust/client/src/ui/widget/action_panel.rs index 2baa711..2b22860 100644 --- a/rust/client/src/ui/widget/action_panel.rs +++ b/rust/client/src/ui/widget/action_panel.rs @@ -158,6 +158,7 @@ fn render_action_panel_items<'a, T: 'a + Clone>( if let Some(title) = title { let text: Element<_> = text(title) + .size(15) .shaping(Shaping::Advanced) .font(Font { weight: Weight::Bold, @@ -200,13 +201,13 @@ fn render_action_panel_items<'a, T: 'a + Clone>( physical_shortcut.as_ref().map(|shortcut| render_shortcut(shortcut)); let content: Element<_> = if let Some(shortcut_element) = shortcut_element { - let text: Element<_> = text(label).shaping(Shaping::Advanced).into(); + let text: Element<_> = text(label).shaping(Shaping::Advanced).size(15).into(); let space: Element<_> = horizontal_space().into(); row([text, space, shortcut_element]).align_y(Alignment::Center).into() } else { - text(label).shaping(Shaping::Advanced).into() + text(label).shaping(Shaping::Advanced).size(15).into() }; let style = match &action_panel_focus_id { diff --git a/rust/client/src/ui/widget/empty_view.rs b/rust/client/src/ui/widget/empty_view.rs index c52af88..d09a98d 100644 --- a/rust/client/src/ui/widget/empty_view.rs +++ b/rust/client/src/ui/widget/empty_view.rs @@ -30,6 +30,7 @@ impl<'b> ComponentWidgets<'b> { None => horizontal_space().into(), Some(subtitle) => { text(subtitle.to_string()) + .size(15) .shaping(Shaping::Advanced) .themed(TextStyle::EmptyViewSubtitle) } diff --git a/rust/client/src/ui/widget/form.rs b/rust/client/src/ui/widget/form.rs index 16c0a67..8e8103f 100644 --- a/rust/client/src/ui/widget/form.rs +++ b/rust/client/src/ui/widget/form.rs @@ -130,6 +130,7 @@ impl<'b> ComponentWidgets<'b> { None => Space::with_width(Length::FillPortion(2)).into(), Some(label) => { let label: Element<_> = text(label.to_string()) + .size(15) .shaping(Shaping::Advanced) .align_x(Horizontal::Right) .width(Length::Fill) diff --git a/rust/client/src/ui/widget/grid.rs b/rust/client/src/ui/widget/grid.rs index 1201d30..66bb8fe 100644 --- a/rust/client/src/ui/widget/grid.rs +++ b/rust/client/src/ui/widget/grid.rs @@ -222,6 +222,7 @@ impl<'b> ComponentWidgets<'b> { if let Some(title) = &widget.title { // TODO text truncation when iced supports it let title = text(title.to_string()) + .size(15) .shaping(Shaping::Advanced) .themed(TextStyle::GridItemTitle); @@ -230,6 +231,7 @@ impl<'b> ComponentWidgets<'b> { if let Some(subtitle) = &widget.subtitle { let subtitle = text(subtitle.to_string()) + .size(15) .shaping(Shaping::Advanced) .themed(TextStyle::GridItemSubTitle); @@ -310,7 +312,7 @@ pub fn render_section<'a>( if let Some(title) = title { let title: Element<_> = text(title.to_string()) .shaping(Shaping::Advanced) - .size(15) + .size(14) .themed(theme_kind_title_text); title_content.push(title) @@ -319,7 +321,7 @@ pub fn render_section<'a>( if let Some(subtitle) = subtitle { let subtitle: Element<_> = text(subtitle.to_string()) .shaping(Shaping::Advanced) - .size(15) + .size(14) .themed(theme_kind_subtitle_text); title_content.push(subtitle) diff --git a/rust/client/src/ui/widget/inline.rs b/rust/client/src/ui/widget/inline.rs index 84132a9..63c86ed 100644 --- a/rust/client/src/ui/widget/inline.rs +++ b/rust/client/src/ui/widget/inline.rs @@ -46,6 +46,7 @@ impl<'b> ComponentWidgets<'b> { entrypoint_name: &str, ) -> Element<'a, ComponentWidgetEvent> { let name: Element<_> = text(format!("{} - {}", plugin_name, entrypoint_name)) + .size(15) .shaping(Shaping::Advanced) .themed(TextStyle::InlineName); diff --git a/rust/client/src/ui/widget/list.rs b/rust/client/src/ui/widget/list.rs index 759b337..9647b38 100644 --- a/rust/client/src/ui/widget/list.rs +++ b/rust/client/src/ui/widget/list.rs @@ -209,7 +209,10 @@ impl<'b> ComponentWidgets<'b> { .as_ref() .map(|icon| render_image(self.data, widget.__id__, icon, None)); - let title: Element<_> = text(widget.title.to_string()).shaping(Shaping::Advanced).into(); + let title: Element<_> = text(widget.title.to_string()) + .shaping(Shaping::Advanced) + .size(15) + .into(); let title: Element<_> = container(title).themed(ContainerStyle::ListItemTitle); let mut content = vec![title]; @@ -222,6 +225,7 @@ impl<'b> ComponentWidgets<'b> { if let Some(subtitle) = &widget.subtitle { let subtitle: Element<_> = text(subtitle.to_string()) + .size(15) .shaping(Shaping::Advanced) .themed(TextStyle::ListItemSubtitle); let subtitle: Element<_> = container(subtitle).themed(ContainerStyle::ListItemSubtitle); diff --git a/rust/client/src/ui/widget/metadata.rs b/rust/client/src/ui/widget/metadata.rs index 9be50ff..9b8f5e3 100644 --- a/rust/client/src/ui/widget/metadata.rs +++ b/rust/client/src/ui/widget/metadata.rs @@ -177,6 +177,7 @@ fn render_metadata_item<'a>( is_in_list: bool, ) -> Element<'a, ComponentWidgetEvent> { let label: Element<_> = text(label.to_string()) + .size(15) .shaping(Shaping::Advanced) .themed(TextStyle::MetadataItemLabel); diff --git a/rust/client/src/ui/widget/root.rs b/rust/client/src/ui/widget/root.rs index 8b7b5be..a241075 100644 --- a/rust/client/src/ui/widget/root.rs +++ b/rust/client/src/ui/widget/root.rs @@ -263,6 +263,7 @@ pub fn render_root<'a, T: 'a + Clone>( let primary_action = match primary_action { Some((label, widget_id, shortcut)) => { let label: Element<_> = text(label) + .size(15) .shaping(Shaping::Advanced) .themed(TextStyle::RootBottomPanelPrimaryActionText); @@ -285,7 +286,9 @@ pub fn render_root<'a, T: 'a + Clone>( let (hide_action_panel, action_panel, bottom_panel) = match action_panel { Some(action_panel) => { - let actions_text: Element<_> = text("Actions").themed(TextStyle::RootBottomPanelActionToggleText); + let actions_text: Element<_> = text("Actions") + .size(15) + .themed(TextStyle::RootBottomPanelActionToggleText); let actions_text: Element<_> = container(actions_text).themed(ContainerStyle::RootBottomPanelActionToggleText); diff --git a/rust/client/src/ui/widget/text.rs b/rust/client/src/ui/widget/text.rs index fdab4b0..2728f74 100644 --- a/rust/client/src/ui/widget/text.rs +++ b/rust/client/src/ui/widget/text.rs @@ -30,7 +30,7 @@ impl<'b> ComponentWidgets<'b> { TextRenderType::H6 => Some(16), }; - let mut text = text(value.join("")).shaping(Shaping::Advanced); + let mut text = text(value.join("")).shaping(Shaping::Advanced).size(15); if let Some(size) = header { text = text.size(size).font(Font { From 4721a70111a431b6f42cc5a4673e22ed502c2448 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 10 Aug 2025 13:13:08 +0200 Subject: [PATCH 79/91] Restrict js heap memory size to 50 mb per plugin --- rust/plugin_runtime/src/deno.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/plugin_runtime/src/deno.rs b/rust/plugin_runtime/src/deno.rs index 8607f88..19470b0 100644 --- a/rust/plugin_runtime/src/deno.rs +++ b/rust/plugin_runtime/src/deno.rs @@ -20,6 +20,7 @@ use deno_core::error::ModuleLoaderError; use deno_core::thiserror; use deno_core::url::ParseError; use deno_core::url::Url; +use deno_core::v8; use deno_error::JsErrorBox; use deno_resolver::npm::ByonmInNpmPackageChecker; use deno_resolver::npm::ManagedNpmResolver; @@ -536,6 +537,7 @@ pub async fn start_js_runtime( should_break_on_first_statement: false, origin_storage_dir: Some(PathBuf::from(init.local_storage_dir)), stdio: Stdio { stdin, stdout, stderr }, + create_params: Some(v8::CreateParams::default().heap_limits(0, 50 * 1024 * 1024)), ..Default::default() }, ); From 204de4d88ce97f1b5cce432e44dc084068e26072 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 10 Aug 2025 21:24:08 +0200 Subject: [PATCH 80/91] Add ability to programmatically control focus on list and grid --- dev_plugin/src/test-grid-focus.tsx | 48 ++++++--- dev_plugin/src/test-list-focus.tsx | 2 +- .../js/components/grid/props/focusedItemId.md | 1 + .../js/components/list/props/focusedItemId.md | 1 + js/api/src/gen/components.tsx | 8 +- rust/client/src/ui/scroll_handle.rs | 10 +- rust/client/src/ui/widget/data.rs | 36 +++++-- rust/client/src/ui/widget/data_mut.rs | 102 +++++++++++++++--- rust/client/src/ui/widget/grid.rs | 38 +++---- rust/client/src/ui/widget/list.rs | 45 +++----- rust/component_model/src/lib.rs | 16 ++- 11 files changed, 205 insertions(+), 102 deletions(-) create mode 100644 docs/js/components/grid/props/focusedItemId.md create mode 100644 docs/js/components/list/props/focusedItemId.md diff --git a/dev_plugin/src/test-grid-focus.tsx b/dev_plugin/src/test-grid-focus.tsx index e61e07b..1a6596a 100644 --- a/dev_plugin/src/test-grid-focus.tsx +++ b/dev_plugin/src/test-grid-focus.tsx @@ -1,27 +1,43 @@ -import { ReactElement } from "react"; -import { Grid } from "@project-gauntlet/api/components"; +import { ReactElement, useState } from "react"; +import { Action, ActionPanel, Grid } from "@project-gauntlet/api/components"; export default function Main(): ReactElement { - const content = ( + const [id, setId] = useState(null); + + const content = (id: string) => ( - Test + {id} ); - + return ( - console.log("onItemFocusChange", id)}> - {content} - {content} - {content} - {content} - {content} - {content} - {content} - {content} - {content} - {content} + + { + console.log(id) + setId("condluran") + }} + /> + + } + > + {content("adarian")} + {content("aruzan")} + {content("blutopian")} + {content("caphex")} + {content("condluran")} + {content("frozian")} + {content("evereni")} + {content("ezaraa")} + {content("houk")} + {content("inleshat")} ) } diff --git a/dev_plugin/src/test-list-focus.tsx b/dev_plugin/src/test-list-focus.tsx index f83ba51..29eb65e 100644 --- a/dev_plugin/src/test-list-focus.tsx +++ b/dev_plugin/src/test-list-focus.tsx @@ -5,7 +5,7 @@ export default function Main(): ReactElement { const [id, setId] = useState(null); return ( - + diff --git a/docs/js/components/grid/props/focusedItemId.md b/docs/js/components/grid/props/focusedItemId.md new file mode 100644 index 0000000..bb4a848 --- /dev/null +++ b/docs/js/components/grid/props/focusedItemId.md @@ -0,0 +1 @@ +Sets item with specified id to be the one that is focused diff --git a/docs/js/components/list/props/focusedItemId.md b/docs/js/components/list/props/focusedItemId.md new file mode 100644 index 0000000..bb4a848 --- /dev/null +++ b/docs/js/components/list/props/focusedItemId.md @@ -0,0 +1 @@ +Sets item with specified id to be the one that is focused diff --git a/js/api/src/gen/components.tsx b/js/api/src/gen/components.tsx index bea973c..d7d28d3 100644 --- a/js/api/src/gen/components.tsx +++ b/js/api/src/gen/components.tsx @@ -151,6 +151,7 @@ declare global { children?: ElementComponent; isLoading?: boolean; onItemFocusChange?: (itemId: string | null) => void; + focusedItemId?: string | null; }; ["gauntlet:grid_item"]: { children?: ElementComponent; @@ -169,6 +170,7 @@ declare global { isLoading?: boolean; columns?: number; onItemFocusChange?: (itemId: string | null) => void; + focusedItemId?: string | null; }; } } @@ -700,6 +702,7 @@ export interface ListProps { actions?: ElementComponent; isLoading?: boolean; onItemFocusChange?: (itemId: string | null) => void; + focusedItemId?: string | null; } export const List: FC & { Item: typeof ListItem; @@ -708,7 +711,7 @@ export const List: FC & { EmptyView: typeof EmptyView; Detail: typeof Detail; } = (props: ListProps): ReactNode => { - return {props.actions as any}{props.children}; + return {props.actions as any}{props.children}; }; List.Item = ListItem; List.Section = ListSection; @@ -746,6 +749,7 @@ export interface GridProps { actions?: ElementComponent; columns?: number; onItemFocusChange?: (itemId: string | null) => void; + focusedItemId?: string | null; } export const Grid: FC & { Item: typeof GridItem; @@ -753,7 +757,7 @@ export const Grid: FC & { SearchBar: typeof SearchBar; EmptyView: typeof EmptyView; } = (props: GridProps): ReactNode => { - return {props.actions as any}{props.children}; + return {props.actions as any}{props.children}; }; Grid.Item = GridItem; Grid.Section = GridSection; diff --git a/rust/client/src/ui/scroll_handle.rs b/rust/client/src/ui/scroll_handle.rs index ec60288..d502bc8 100644 --- a/rust/client/src/ui/scroll_handle.rs +++ b/rust/client/src/ui/scroll_handle.rs @@ -65,6 +65,13 @@ impl ScrollHandle { } } + pub fn from(scroll_handle: &ScrollHandle, current_item_id: Option) -> ScrollHandle { + ScrollHandle { + scrollable_id: scroll_handle.scrollable_id.clone(), + current_item_id, + } + } + pub fn get<'a, T>(&self, items: &'a ScrollContent) -> Option<&'a T> { self.get_by_id(items, self.current_item_id.clone()) } @@ -121,7 +128,8 @@ impl ScrollHandle { grid: Vec>, ) -> (Option, Option>) { let Some(current_item_id) = &self.current_item_id else { - return (None, self.focus_target(behavior.unfocused(grid))); + let target_item_id = behavior.unfocused(grid); + return (target_item_id.clone(), self.focus_target(target_item_id)); }; let Some((row_index, row)) = grid.iter().find_position(|row| row.contains(¤t_item_id)) else { diff --git a/rust/client/src/ui/widget/data.rs b/rust/client/src/ui/widget/data.rs index 1b659d6..f6d171d 100644 --- a/rust/client/src/ui/widget/data.rs +++ b/rust/client/src/ui/widget/data.rs @@ -6,6 +6,7 @@ use gauntlet_common::model::ActionPanelWidgetOrderedMembers; use gauntlet_common::model::GridSectionWidgetOrderedMembers; use gauntlet_common::model::GridWidget; use gauntlet_common::model::GridWidgetOrderedMembers; +use gauntlet_common::model::JsOption; use gauntlet_common::model::ListSectionWidgetOrderedMembers; use gauntlet_common::model::ListWidget; use gauntlet_common::model::ListWidgetOrderedMembers; @@ -27,7 +28,6 @@ use crate::ui::widget::action_panel::convert_action_panel; use crate::ui::widget::data_mut::ComponentWidgetsMut; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::state::ComponentWidgetStateContainer; -use crate::ui::widget::state::ScrollableRootState; use crate::ui::widget::state::TextFieldState; #[derive(Debug)] @@ -115,20 +115,34 @@ impl<'b> ComponentWidgets<'b> { RootWidgetMembers::Form(_) => None, RootWidgetMembers::Inline(_) => None, RootWidgetMembers::List(widget) => { - let ScrollableRootState { - scroll_handle: focused_item, - .. - } = self.state.scrollable_root_state(widget.__id__); + match &widget.focused_item_id { + JsOption::Undefined => { + let state = self.state.scrollable_root_state(widget.__id__); - self.list_focused_item_id(focused_item, focused_item.current_item_id.clone(), widget) + self.list_focused_item_id( + &state.scroll_handle, + state.scroll_handle.current_item_id.clone(), + widget, + ) + } + JsOption::Null => None, + JsOption::Value(value) => Some(value.clone()), + } } RootWidgetMembers::Grid(widget) => { - let ScrollableRootState { - scroll_handle: focused_item, - .. - } = self.state.scrollable_root_state(widget.__id__); + match &widget.focused_item_id { + JsOption::Undefined => { + let state = self.state.scrollable_root_state(widget.__id__); - self.grid_focused_item_id(focused_item, focused_item.current_item_id.clone(), widget) + self.grid_focused_item_id( + &state.scroll_handle, + state.scroll_handle.current_item_id.clone(), + widget, + ) + } + JsOption::Null => None, + JsOption::Value(value) => Some(value.clone()), + } } } } diff --git a/rust/client/src/ui/widget/data_mut.rs b/rust/client/src/ui/widget/data_mut.rs index 7e41780..55335f6 100644 --- a/rust/client/src/ui/widget/data_mut.rs +++ b/rust/client/src/ui/widget/data_mut.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use gauntlet_common::model::GridSectionWidgetOrderedMembers; use gauntlet_common::model::GridWidget; use gauntlet_common::model::GridWidgetOrderedMembers; +use gauntlet_common::model::JsOption; use gauntlet_common::model::ListSectionWidgetOrderedMembers; use gauntlet_common::model::ListWidget; use gauntlet_common::model::ListWidgetOrderedMembers; @@ -20,6 +21,7 @@ use iced::widget::text_input; use itertools::Itertools; use crate::ui::AppMsg; +use crate::ui::scroll_handle::ScrollHandle; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::grid::grid_width; use crate::ui::widget::state::ComponentWidgetStateContainer; @@ -172,8 +174,8 @@ impl<'b> ComponentWidgetsMut<'b> { RootWidgetMembers::Inline(_) => Task::none(), RootWidgetMembers::List(widget) => { let ids = self.list_collect_ids(&widget); - let state = self.state.scrollable_root_state_mut(widget.__id__); - let (next_item, Some(focus_task)) = state.scroll_handle.list_focus_up(ids) else { + let mut scroll_handle = self.list_scroll_handle(widget); + let (next_item, Some(focus_task)) = scroll_handle.list_focus_up(ids) else { return Task::none(); }; @@ -189,8 +191,8 @@ impl<'b> ComponentWidgetsMut<'b> { } RootWidgetMembers::Grid(widget) => { let ids = self.grid_collect_ids(widget); - let state = self.state.scrollable_root_state_mut(widget.__id__); - let (next_item, Some(focus_task)) = state.scroll_handle.grid_focus_up(ids) else { + let mut scroll_handle = self.grid_scroll_handle(widget); + let (next_item, Some(focus_task)) = scroll_handle.grid_focus_up(ids) else { return Task::none(); }; @@ -221,8 +223,8 @@ impl<'b> ComponentWidgetsMut<'b> { RootWidgetMembers::Form(_) => Task::none(), RootWidgetMembers::List(widget) => { let ids = self.list_collect_ids(widget); - let state = self.state.scrollable_root_state_mut(widget.__id__); - let (next_item, Some(focus_task)) = state.scroll_handle.list_focus_down(ids) else { + let mut scroll_handle = self.list_scroll_handle(widget); + let (next_item, Some(focus_task)) = scroll_handle.list_focus_down(ids) else { return Task::none(); }; @@ -243,8 +245,8 @@ impl<'b> ComponentWidgetsMut<'b> { }; let ids = self.grid_collect_ids(widget); - let state = self.state.scrollable_root_state_mut(widget.__id__); - let (next_item, Some(focus_task)) = state.scroll_handle.grid_focus_down(ids) else { + let mut scroll_handle = self.grid_scroll_handle(widget); + let (next_item, Some(focus_task)) = scroll_handle.grid_focus_down(ids) else { return Task::none(); }; @@ -278,8 +280,8 @@ impl<'b> ComponentWidgetsMut<'b> { RootWidgetMembers::List(_) => Task::none(), RootWidgetMembers::Grid(widget) => { let ids = self.grid_collect_ids(widget); - let state = self.state.scrollable_root_state_mut(widget.__id__); - let (next_item, Some(focus_task)) = state.scroll_handle.grid_focus_left(ids) else { + let mut scroll_handle = self.grid_scroll_handle(widget); + let (next_item, Some(focus_task)) = scroll_handle.grid_focus_left(ids) else { return Task::none(); }; @@ -312,8 +314,8 @@ impl<'b> ComponentWidgetsMut<'b> { RootWidgetMembers::List(_) => Task::none(), RootWidgetMembers::Grid(widget) => { let ids = self.grid_collect_ids(widget); - let state = self.state.scrollable_root_state_mut(widget.__id__); - let (next_item, Some(focus_task)) = state.scroll_handle.grid_focus_right(ids) else { + let mut scroll_handle = self.grid_scroll_handle(widget); + let (next_item, Some(focus_task)) = scroll_handle.grid_focus_right(ids) else { return Task::none(); }; @@ -443,4 +445,80 @@ impl<'b> ComponentWidgetsMut<'b> { result } + + pub fn list_id_for_id(&self, widget: &ListWidget, target_item_id: &String) -> Option { + for members in &widget.content.ordered_members { + match &members { + ListWidgetOrderedMembers::ListItem(item) => { + let state = self.state.scrollable_item_state(item.__id__); + if &item.id == target_item_id { + return Some(state.id.clone()); + } + } + ListWidgetOrderedMembers::ListSection(section) => { + for members in §ion.content.ordered_members { + match &members { + ListSectionWidgetOrderedMembers::ListItem(item) => { + let state = self.state.scrollable_item_state(item.__id__); + if &item.id == target_item_id { + return Some(state.id.clone()); + } + } + } + } + } + } + } + + None + } + + pub fn grid_id_for_id(&self, widget: &GridWidget, target_item_id: &String) -> Option { + for members in &widget.content.ordered_members { + match &members { + GridWidgetOrderedMembers::GridItem(item) => { + let state = self.state.scrollable_item_state(item.__id__); + if &item.id == target_item_id { + return Some(state.id.clone()); + } + } + GridWidgetOrderedMembers::GridSection(section) => { + for members in §ion.content.ordered_members { + match &members { + GridSectionWidgetOrderedMembers::GridItem(item) => { + let state = self.state.scrollable_item_state(item.__id__); + if &item.id == target_item_id { + return Some(state.id.clone()); + } + } + } + } + } + } + } + + None + } + + fn grid_scroll_handle(&self, widget: &GridWidget) -> ScrollHandle { + let state = self.state.scrollable_root_state(widget.__id__); + match &widget.focused_item_id { + JsOption::Undefined => state.scroll_handle.clone(), + JsOption::Null => ScrollHandle::from(&state.scroll_handle, None), + JsOption::Value(focused_item_id) => { + ScrollHandle::from(&state.scroll_handle, self.grid_id_for_id(widget, focused_item_id)) + } + } + } + + fn list_scroll_handle(&self, widget: &ListWidget) -> ScrollHandle { + let state = self.state.scrollable_root_state(widget.__id__); + match &widget.focused_item_id { + JsOption::Undefined => state.scroll_handle.clone(), + JsOption::Null => ScrollHandle::from(&state.scroll_handle, None), + JsOption::Value(focused_item_id) => { + ScrollHandle::from(&state.scroll_handle, self.list_id_for_id(widget, focused_item_id)) + } + } + } } diff --git a/rust/client/src/ui/widget/grid.rs b/rust/client/src/ui/widget/grid.rs index 66bb8fe..6c76364 100644 --- a/rust/client/src/ui/widget/grid.rs +++ b/rust/client/src/ui/widget/grid.rs @@ -30,7 +30,6 @@ use crate::ui::theme::text::TextStyle; use crate::ui::widget::accessories::render_icon_accessory; use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; -use crate::ui::widget::state::ScrollableRootState; impl<'b> ComponentWidgets<'b> { pub fn render_grid_widget<'a>( @@ -40,10 +39,8 @@ impl<'b> ComponentWidgets<'b> { entrypoint_name: &str, action_shortcuts: &HashMap, ) -> Element<'a, ComponentWidgetEvent> { - let ScrollableRootState { - show_action_panel, - scroll_handle, - } = self.state.scrollable_root_state(grid_widget.__id__); + let state = self.state.scrollable_root_state(grid_widget.__id__); + let focused_item_id = self.get_focused_item_id(); let content = if grid_widget.content.ordered_members.is_empty() { match &grid_widget.content.empty_view { @@ -67,7 +64,7 @@ impl<'b> ComponentWidgets<'b> { let content = self.render_grid_section( &pending, &grid_widget.columns, - scroll_handle.current_item_id.clone(), + &focused_item_id, index_counter, ); @@ -78,7 +75,7 @@ impl<'b> ComponentWidgets<'b> { items.push(self.render_grid_section_widget( widget, - scroll_handle.current_item_id.clone(), + &focused_item_id, index_counter, first_section, )); @@ -89,12 +86,7 @@ impl<'b> ComponentWidgets<'b> { } if !pending.is_empty() { - let content = self.render_grid_section( - &pending, - &grid_widget.columns, - scroll_handle.current_item_id.clone(), - index_counter, - ); + let content = self.render_grid_section(&pending, &grid_widget.columns, &focused_item_id, index_counter); items.push(content); } @@ -104,7 +96,7 @@ impl<'b> ComponentWidgets<'b> { let content: Element<_> = container(content).width(Length::Fill).themed(ContainerStyle::GridInner); let content: Element<_> = scrollable(content) - .id(scroll_handle.scrollable_id.clone()) + .id(state.scroll_handle.scrollable_id.clone()) .width(Length::Fill) .into(); @@ -113,11 +105,8 @@ impl<'b> ComponentWidgets<'b> { content }; - let focused_item_id = - self.grid_focused_item_id(scroll_handle, scroll_handle.current_item_id.clone(), grid_widget); - self.render_plugin_root( - *show_action_panel, + state.show_action_panel, grid_widget.__id__, focused_item_id, &grid_widget.content.search_bar, @@ -133,7 +122,7 @@ impl<'b> ComponentWidgets<'b> { fn render_grid_section_widget<'a>( &self, widget: &GridSectionWidget, - item_focused_id: Option, + item_focused_id: &Option, index_counter: &Cell, first_section: bool, ) -> Element<'a, ComponentWidgetEvent> { @@ -169,7 +158,7 @@ impl<'b> ComponentWidgets<'b> { fn render_grid_item_widget<'a>( &self, widget: &GridItemWidget, - item_focused_id: Option, + item_focused_id: &Option, index_counter: &Cell, grid_width: usize, ) -> Element<'a, ComponentWidgetEvent> { @@ -183,8 +172,6 @@ impl<'b> ComponentWidgets<'b> { 8.. => 50, }; - let state = self.state.scrollable_item_state(widget.__id__); - let content: Element<_> = container(self.render_content_widget(&widget.content.content, true)) .height(height) .into(); @@ -192,7 +179,7 @@ impl<'b> ComponentWidgets<'b> { let style = match &item_focused_id { None => ButtonStyle::GridItem, Some(focused_index) => { - if focused_index == &state.id { + if focused_index == &widget.id { ButtonStyle::GridItemFocused } else { ButtonStyle::GridItem @@ -251,6 +238,7 @@ impl<'b> ComponentWidgets<'b> { let content: Element<_> = column(vec![content, sub_content]).width(Length::Fill).into(); + let state = self.state.scrollable_item_state(widget.__id__); let content: Element<_> = container(content).id(state.id.clone()).into(); content @@ -261,7 +249,7 @@ impl<'b> ComponentWidgets<'b> { items: &[&GridItemWidget], /*aspect_ratio: Option<&str>,*/ columns: &Option, - item_focused_id: Option, + item_focused_id: &Option, index_counter: &Cell, ) -> Element<'a, ComponentWidgetEvent> { // TODO @@ -281,7 +269,7 @@ impl<'b> ComponentWidgets<'b> { let rows: Vec> = items .iter() - .map(|widget| self.render_grid_item_widget(widget, item_focused_id.clone(), index_counter, columns)) + .map(|widget| self.render_grid_item_widget(widget, item_focused_id, index_counter, columns)) .chunks(columns) .into_iter() .flat_map(|row_items| row_items) diff --git a/rust/client/src/ui/widget/list.rs b/rust/client/src/ui/widget/list.rs index 9647b38..b2e5695 100644 --- a/rust/client/src/ui/widget/list.rs +++ b/rust/client/src/ui/widget/list.rs @@ -33,7 +33,6 @@ use crate::ui::widget::data::ComponentWidgets; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::grid::render_section; use crate::ui::widget::images::render_image; -use crate::ui::widget::state::ScrollableRootState; impl<'b> ComponentWidgets<'b> { pub fn render_list_widget<'a>( @@ -44,10 +43,8 @@ impl<'b> ComponentWidgets<'b> { action_shortcuts: &HashMap, ) -> Element<'a, ComponentWidgetEvent> { let widget_id = list_widget.__id__; - let ScrollableRootState { - show_action_panel, - scroll_handle, - } = self.state.scrollable_root_state(widget_id); + let state = self.state.scrollable_root_state(widget_id); + let focused_item_id = self.get_focused_item_id(); let mut pending: Vec<&ListItemWidget> = vec![]; let mut items: Vec> = vec![]; @@ -64,13 +61,7 @@ impl<'b> ComponentWidgets<'b> { if !pending.is_empty() { let content: Vec<_> = pending .iter() - .map(|widget| { - self.render_list_item_widget( - widget, - scroll_handle.current_item_id.clone(), - index_counter, - ) - }) + .map(|widget| self.render_list_item_widget(widget, &focused_item_id, index_counter)) .collect(); let content: Element<_> = column(content).into(); @@ -80,12 +71,7 @@ impl<'b> ComponentWidgets<'b> { pending = vec![]; } - items.push(self.render_list_section_widget( - widget, - scroll_handle.current_item_id.clone(), - index_counter, - first_section, - )); + items.push(self.render_list_section_widget(widget, &focused_item_id, index_counter, first_section)); first_section = false; } @@ -95,9 +81,7 @@ impl<'b> ComponentWidgets<'b> { if !pending.is_empty() { let content: Vec<_> = pending .iter() - .map(|widget| { - self.render_list_item_widget(widget, scroll_handle.current_item_id.clone(), index_counter) - }) + .map(|widget| self.render_list_item_widget(widget, &focused_item_id, index_counter)) .collect(); let content: Element<_> = column(content).into(); @@ -116,7 +100,7 @@ impl<'b> ComponentWidgets<'b> { let content: Element<_> = container(content).width(Length::Fill).themed(ContainerStyle::ListInner); let content: Element<_> = scrollable(content) - .id(scroll_handle.scrollable_id.clone()) + .id(state.scroll_handle.scrollable_id.clone()) .width(Length::Fill) .into(); @@ -143,11 +127,8 @@ impl<'b> ComponentWidgets<'b> { let content: Element<_> = row(elements).height(Length::Fill).into(); - let focused_item_id = - self.list_focused_item_id(scroll_handle, scroll_handle.current_item_id.clone(), list_widget); - self.render_plugin_root( - *show_action_panel, + state.show_action_panel, widget_id, focused_item_id, &list_widget.content.search_bar, @@ -163,7 +144,7 @@ impl<'b> ComponentWidgets<'b> { fn render_list_section_widget<'a>( &self, widget: &ListSectionWidget, - item_focused_id: Option, + item_focused_id: &Option, index_counter: &Cell, first_section: bool, ) -> Element<'a, ComponentWidgetEvent> { @@ -174,7 +155,7 @@ impl<'b> ComponentWidgets<'b> { .map(|members| { match members { ListSectionWidgetOrderedMembers::ListItem(widget) => { - self.render_list_item_widget(widget, item_focused_id.clone(), index_counter) + self.render_list_item_widget(widget, item_focused_id, index_counter) } } }) @@ -201,7 +182,7 @@ impl<'b> ComponentWidgets<'b> { fn render_list_item_widget<'a>( &self, widget: &ListItemWidget, - item_focused_id: Option, + item_focused_id: &Option, index_counter: &Cell, ) -> Element<'a, ComponentWidgetEvent> { let icon: Option> = widget @@ -256,12 +237,10 @@ impl<'b> ComponentWidgets<'b> { let content: Element<_> = row(content).align_y(Alignment::Center).into(); - let state = self.state.scrollable_item_state(widget.__id__); - let style = match &item_focused_id { None => ButtonStyle::ListItem, Some(focused_index) => { - if focused_index == &state.id { + if focused_index == &widget.id { ButtonStyle::ListItemFocused } else { ButtonStyle::ListItem @@ -286,6 +265,8 @@ impl<'b> ComponentWidgets<'b> { let element = button(content).on_press(on_press_msg).width(Length::Fill).themed(style); + let state = self.state.scrollable_item_state(widget.__id__); + container(element).id(state.id.clone()).into() } } diff --git a/rust/component_model/src/lib.rs b/rust/component_model/src/lib.rs index 8210a4e..2bb3136 100644 --- a/rust/component_model/src/lib.rs +++ b/rust/component_model/src/lib.rs @@ -958,7 +958,7 @@ pub fn create_component_model() -> Vec { [ property( "isLoading", - mark_doc!("/list/props/isLoading.md"), + mark_doc!("/detail/props/isLoading.md"), OptionalKind::Yes, PropertyType::Boolean, ), @@ -1181,7 +1181,7 @@ pub fn create_component_model() -> Vec { [ property( "isLoading", - mark_doc!("/list/props/isLoading.md"), + mark_doc!("/form/props/isLoading.md"), OptionalKind::Yes, PropertyType::Boolean, ), @@ -1453,6 +1453,12 @@ pub fn create_component_model() -> Vec { PropertyType::String, )], ), + property( + "focusedItemId", + mark_doc!("/list/props/focusedItemId.md"), + OptionalKind::YesButComplicated, + PropertyType::String, + ), ], children_members( [ @@ -1566,6 +1572,12 @@ pub fn create_component_model() -> Vec { PropertyType::String, )], ), + property( + "focusedItemId", + mark_doc!("/grid/props/focusedItemId.md"), + OptionalKind::YesButComplicated, + PropertyType::String, + ), ], children_members( [ From 24405c92bca33f9f93cb08aea407f9bd0ad7ba55 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sun, 10 Aug 2025 21:30:20 +0200 Subject: [PATCH 81/91] Extend focusedItemId description --- docs/js/components/grid/props/focusedItemId.md | 2 +- docs/js/components/list/props/focusedItemId.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/js/components/grid/props/focusedItemId.md b/docs/js/components/grid/props/focusedItemId.md index bb4a848..07b667d 100644 --- a/docs/js/components/grid/props/focusedItemId.md +++ b/docs/js/components/grid/props/focusedItemId.md @@ -1 +1 @@ -Sets item with specified id to be the one that is focused +Sets item with specified id to be the one that is focused. `undefined` - uncontrolled, `null` - controlled and unset, other values - controlled and set diff --git a/docs/js/components/list/props/focusedItemId.md b/docs/js/components/list/props/focusedItemId.md index bb4a848..07b667d 100644 --- a/docs/js/components/list/props/focusedItemId.md +++ b/docs/js/components/list/props/focusedItemId.md @@ -1 +1 @@ -Sets item with specified id to be the one that is focused +Sets item with specified id to be the one that is focused. `undefined` - uncontrolled, `null` - controlled and unset, other values - controlled and set From 1a2368ffde06b74de09a4a7dad7371f5fb240824 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 15 Aug 2025 16:00:06 +0200 Subject: [PATCH 82/91] Fix Opened Windows view often not showing any windows --- .../gauntlet/src/window/shared.tsx | 26 +++++++++++++------ bundled_plugins/gauntlet/src/windows.tsx | 10 +++++-- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/bundled_plugins/gauntlet/src/window/shared.tsx b/bundled_plugins/gauntlet/src/window/shared.tsx index 235b2ef..8a3447e 100644 --- a/bundled_plugins/gauntlet/src/window/shared.tsx +++ b/bundled_plugins/gauntlet/src/window/shared.tsx @@ -4,7 +4,6 @@ import { GeneratedEntrypointAction, } from "@project-gauntlet/api/helpers"; import { linux_open_application } from "gauntlet:bridge/internal-linux"; -import React from "react"; import { Action, ActionPanel, List } from "@project-gauntlet/api/components"; export function ListOfWindows({ windows, focusWindow }: { @@ -45,7 +44,13 @@ export type OpenWindowData = { appId: string } -export const openWindows: Record = {}; + +export function openWindows(): Record { + if ((globalThis as any).__openWindows == undefined) { + (globalThis as any).__openWindows = {} + } + return (globalThis as any).__openWindows +} export function applicationActions( id: string, @@ -65,7 +70,7 @@ export function applicationActions( } const appWindows = Object.fromEntries( - Object.entries(openWindows) + Object.entries(openWindows()) .filter(([_, windowData]) => windowData.appId == id) ) @@ -103,7 +108,12 @@ export function applicationActions( { label: "Show windows", view: () => { - return focusWindow(windowId)}/> + return ( + focusWindow(windowId)} + /> + ) } }, { @@ -123,7 +133,7 @@ export function applicationAccessories(id: string, experimentalWindowTracking: b return [] } - const appWindows = Object.entries(openWindows) + const appWindows = Object.entries(openWindows()) .filter(([_, windowData]) => windowData.appId == id) if (appWindows.length == 0) { @@ -147,7 +157,7 @@ export function addOpenWindow( add: (id: string, data: GeneratedEntrypoint) => void, ) { if (generatedEntrypoint) { - openWindows[windowId] = { + openWindows()[windowId] = { id: windowId, appId: appId, title: windowTitle @@ -172,11 +182,11 @@ export function deleteOpenWindow( get: (id: string) => GeneratedEntrypoint | undefined, add: (id: string, data: GeneratedEntrypoint) => void, ) { - const openWindow = openWindows[windowId]; + const openWindow = openWindows()[windowId]; if (openWindow) { const generatedEntrypoint = get(openWindow.appId); - delete openWindows[windowId]; + delete openWindows()[windowId]; const knownWindows = readWindowOrder(); const newKnownWindows = knownWindows.filter(id => id != windowId) diff --git a/bundled_plugins/gauntlet/src/windows.tsx b/bundled_plugins/gauntlet/src/windows.tsx index 5f9a644..a974598 100644 --- a/bundled_plugins/gauntlet/src/windows.tsx +++ b/bundled_plugins/gauntlet/src/windows.tsx @@ -10,11 +10,17 @@ export default function Windows(): ReactElement { case "linux": { if (wayland()) { return ( - focusWaylandWindow(windowId)}/> + focusWaylandWindow(windowId)} + /> ) } else { return ( - focusX11Window(windowId)}/> + focusX11Window(windowId)} + /> ) } } From b008fcd907bd9a395ee97c99926bbd58b4c524ee Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 15 Aug 2025 16:04:47 +0200 Subject: [PATCH 83/91] Focus second item in Opened windows view as an alternative window --- bundled_plugins/gauntlet/src/window/shared.tsx | 13 +++++++++++-- bundled_plugins/gauntlet/src/windows.tsx | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/bundled_plugins/gauntlet/src/window/shared.tsx b/bundled_plugins/gauntlet/src/window/shared.tsx index 8a3447e..b6ba0a9 100644 --- a/bundled_plugins/gauntlet/src/window/shared.tsx +++ b/bundled_plugins/gauntlet/src/window/shared.tsx @@ -4,17 +4,23 @@ import { GeneratedEntrypointAction, } from "@project-gauntlet/api/helpers"; import { linux_open_application } from "gauntlet:bridge/internal-linux"; +import { useState } from "react"; import { Action, ActionPanel, List } from "@project-gauntlet/api/components"; -export function ListOfWindows({ windows, focusWindow }: { +export function ListOfWindows({ windows, focusWindow, focusSecond }: { windows: Record, - focusWindow: (windowId: string) => void + focusWindow: (windowId: string) => void, + focusSecond: boolean }) { const knownWindows = readWindowOrder(); const sortedWindows = Object.keys(windows) // sort windows based on array stored on storage .sort((a, b) => knownWindows.indexOf(a) - knownWindows.indexOf(b)); + const [id, setId] = useState( + focusSecond ? sortedWindows.at(1) || null : null + ); + return ( } + onItemFocusChange={setId} + focusedItemId={id} > { sortedWindows.map(window => ) @@ -112,6 +120,7 @@ export function applicationActions( focusWindow(windowId)} + focusSecond={false} /> ) } diff --git a/bundled_plugins/gauntlet/src/windows.tsx b/bundled_plugins/gauntlet/src/windows.tsx index a974598..8037b42 100644 --- a/bundled_plugins/gauntlet/src/windows.tsx +++ b/bundled_plugins/gauntlet/src/windows.tsx @@ -13,6 +13,7 @@ export default function Windows(): ReactElement { focusWaylandWindow(windowId)} + focusSecond={true} /> ) } else { @@ -20,6 +21,7 @@ export default function Windows(): ReactElement { focusX11Window(windowId)} + focusSecond={true} /> ) } From a577202ec91e69d2f256465b3f3dbc87a7e7d7e2 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 15 Aug 2025 17:00:24 +0200 Subject: [PATCH 84/91] Implement native hud notifications on Linux, enable by default --- Cargo.lock | 28 ++++++++++++-- dev_data/config/config.toml | 3 ++ rust/server/Cargo.toml | 1 + rust/server/src/plugins/js.rs | 43 +++++++++++++++++++++- rust/server/src/plugins/mod.rs | 1 + rust/server/src/plugins/settings/config.rs | 7 ++++ rust/server/src/plugins/settings/mod.rs | 3 ++ 7 files changed, 81 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d80748..325f668 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -375,6 +375,23 @@ dependencies = [ "zbus 5.7.1", ] +[[package]] +name = "ashpd" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.1", + "serde", + "serde_repr", + "tokio", + "url", + "zbus 5.7.1", +] + [[package]] name = "asn1-rs" version = "0.5.2" @@ -2038,7 +2055,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18e1a09f280e29a8b00bc7e81eca5ac87dca0575639c9422a5fa25a07bb884b8" dependencies = [ - "ashpd", + "ashpd 0.10.3", "async-std", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3511,7 +3528,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.7.4", + "libloading 0.8.8", ] [[package]] @@ -4677,6 +4694,7 @@ version = "0.0.0" dependencies = [ "anyhow", "arboard", + "ashpd 0.11.0", "bytes", "dark-light 1.1.1", "futures", @@ -7485,7 +7503,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", "syn 2.0.103", @@ -11397,6 +11415,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", + "tracing", "windows-sys 0.52.0", ] @@ -12789,7 +12808,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -13693,6 +13712,7 @@ dependencies = [ "ordered-stream", "serde", "serde_repr", + "tokio", "tracing", "uds_windows", "windows-sys 0.59.0", diff --git a/dev_data/config/config.toml b/dev_data/config/config.toml index 6deeabd..01ebd52 100644 --- a/dev_data/config/config.toml +++ b/dev_data/config/config.toml @@ -6,3 +6,6 @@ #main_window_surface = "xdg_shell" global_shortcuts_api = "legacy_x11_api" #global_shortcuts_api = "none" + +[linux] +native_hud = true diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index 54ce89c..980dbdf 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -44,6 +44,7 @@ url = "2.5" ureq = "2.10" dark-light = "1.1.1" schemars = "0.8" +ashpd = "0.11" [features] release = ["gauntlet-common/release", "gauntlet-plugin-runtime/release"] diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index b0398a3..d675d84 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; use std::fs::File; use std::sync::Arc; +use std::time::Duration; +use std::vec; use anyhow::Context; use anyhow::anyhow; @@ -50,6 +52,7 @@ use serde::Serialize; use tokio::sync::Mutex; use crate::model::IntermediateUiEvent; +use crate::plugins::Settings; use crate::plugins::binary_data_gatherer::BinaryDataGatherer; use crate::plugins::clipboard::Clipboard; use crate::plugins::data_db_repository::DataDbRepository; @@ -78,6 +81,7 @@ pub struct PluginRuntimeData { pub db_repository: DataDbRepository, pub search_index: SearchIndex, pub icon_cache: IconCache, + pub settings: Settings, pub frontend_api: FrontendApiProxy, pub dirs: Dirs, pub clipboard: Clipboard, @@ -159,6 +163,7 @@ pub async fn start_plugin_runtime(data: PluginRuntimeData, run_status_guard: Run data.search_index, data.clipboard, data.frontend_api, + data.settings, data.uuid.clone(), data.id.clone(), data.name, @@ -601,6 +606,7 @@ pub struct BackendForPluginRuntimeApiImpl { search_index: SearchIndex, clipboard: Clipboard, frontend_api: FrontendApiProxy, + settings: Settings, #[allow(unused)] plugin_uuid: String, plugin_id: PluginId, @@ -614,6 +620,7 @@ impl BackendForPluginRuntimeApiImpl { search_index: SearchIndex, clipboard: Clipboard, frontend_api: FrontendApiProxy, + settings: Settings, plugin_uuid: String, plugin_id: PluginId, plugin_name: String, @@ -624,6 +631,7 @@ impl BackendForPluginRuntimeApiImpl { search_index, clipboard, frontend_api, + settings, plugin_uuid, plugin_id, plugin_name, @@ -961,7 +969,16 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { } async fn ui_show_hud(&self, display: String) -> RequestResult<()> { - self.frontend_api.show_hud(display).await?; + if cfg!(target_os = "linux") { + if self.settings.config().linux_native_hud { + #[cfg(target_os = "linux")] + show_linux_native_hud(display).await?; + } else { + self.frontend_api.show_hud(display).await?; + } + } else { + self.frontend_api.show_hud(display).await?; + } Ok(()) } @@ -1201,3 +1218,27 @@ fn any_preferences_missing_value( false } + +#[cfg(target_os = "linux")] +async fn show_linux_native_hud(display: String) -> anyhow::Result<()> { + use ashpd::desktop::notification::DisplayHint; + use ashpd::desktop::notification::Notification; + use ashpd::desktop::notification::NotificationProxy; + use ashpd::desktop::notification::Priority; + + let proxy = NotificationProxy::new().await?; + + let notification_id = "sh.gauntlet.Hud"; + let notification = Notification::new(&display) + .priority(Priority::Urgent) + .display_hint(vec![DisplayHint::Transient, DisplayHint::ShowAsNew]); + + proxy.add_notification(notification_id, notification).await?; + + tokio::spawn(async move { + tokio::time::sleep(Duration::from_secs(2)).await; + let _ = proxy.remove_notification(notification_id).await; + }); + + Ok(()) +} diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index dfe8b50..c5c2a3e 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -919,6 +919,7 @@ impl ApplicationManager { search_index: self.search_index.clone(), icon_cache: self.icon_cache.clone(), frontend_api: self.frontend_api.clone(), + settings: self.settings.clone(), dirs: self.dirs.clone(), clipboard: self.clipboard.clone(), }; diff --git a/rust/server/src/plugins/settings/config.rs b/rust/server/src/plugins/settings/config.rs index f6c30db..d2fa862 100644 --- a/rust/server/src/plugins/settings/config.rs +++ b/rust/server/src/plugins/settings/config.rs @@ -5,6 +5,7 @@ use serde::Deserialize; pub struct ApplicationConfig { pub main_window: Option, pub wayland: Option, + pub linux: Option, } #[derive(Deserialize, Debug, Default)] @@ -18,6 +19,11 @@ pub struct WaylandConfig { pub global_shortcuts_api: Option, } +#[derive(Deserialize, Debug, Default)] +pub struct LinuxConfig { + pub native_hud: Option, +} + #[derive(Deserialize, Debug)] pub enum WaylandMainWindowConfig { #[serde(rename = "prefer_wlr_layer_shell")] // default @@ -40,4 +46,5 @@ pub struct EffectiveConfig { pub close_on_unfocus: bool, pub layer_shell: bool, pub wayland_use_legacy_x11_api: bool, + pub linux_native_hud: bool, } diff --git a/rust/server/src/plugins/settings/mod.rs b/rust/server/src/plugins/settings/mod.rs index 7e23978..b2dc7a9 100644 --- a/rust/server/src/plugins/settings/mod.rs +++ b/rust/server/src/plugins/settings/mod.rs @@ -298,8 +298,10 @@ impl Settings { fn effective_config(config: ApplicationConfig, layer_shell_supported: bool) -> EffectiveConfig { let window_config = config.main_window.unwrap_or_default(); let wayland_config = config.wayland.unwrap_or_default(); + let linux_config = config.linux.unwrap_or_default(); let close_on_unfocus = window_config.close_on_unfocus.unwrap_or(true); + let linux_native_hud = linux_config.native_hud.unwrap_or(true); let main_window_surface = wayland_config .main_window_surface @@ -324,5 +326,6 @@ fn effective_config(config: ApplicationConfig, layer_shell_supported: bool) -> E close_on_unfocus, layer_shell, wayland_use_legacy_x11_api, + linux_native_hud, } } From 3516d35a35f676d09f7be547e67f1bacabdc36be Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 15 Aug 2025 17:24:49 +0200 Subject: [PATCH 85/91] Fix windows build --- rust/server/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index 980dbdf..5129af7 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -44,6 +44,8 @@ url = "2.5" ureq = "2.10" dark-light = "1.1.1" schemars = "0.8" + +[target.'cfg(target_os = "linux")'.dependencies] ashpd = "0.11" [features] From 1116190f7dbc79e6e55f25f723555d2b1169bbb7 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 15 Aug 2025 18:13:53 +0200 Subject: [PATCH 86/91] Use lucide icons for shortcuts widget --- Cargo.toml | 4 +-- rust/client/src/ui/mod.rs | 2 ++ rust/client/src/ui/widget/action_panel.rs | 4 +-- rust/common_ui/src/lib.rs | 35 +++++++++++------------ rust/server/src/plugins/js.rs | 3 +- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eb719e3..9881fcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,8 +26,8 @@ edition = "2024" # iced #iced = { version = "0.13.99", features = ["wgpu", "tiny-skia", "web-colors", "tokio", "lazy", "advanced", "image", "svg"] } iced = { git = "https://github.com/project-gauntlet/iced.git", branch = "gauntlet-0.13.1", features = ["wgpu", "tiny-skia", "web-colors", "tokio", "lazy", "advanced", "image", "svg"] } -#iced_fonts = { version = "0.2.99", features = ["bootstrap"] } -iced_fonts = { git = "https://github.com/project-gauntlet/iced_fonts.git", branch = "gauntlet-0.13.1", features = ["bootstrap"] } +#iced_fonts = { version = "0.2.99", features = ["bootstrap", "lucide"] } +iced_fonts = { git = "https://github.com/project-gauntlet/iced_fonts.git", branch = "gauntlet-0.13.1", features = ["bootstrap", "lucide"] } # workspaces gauntlet-common = { path = "./rust/common" } diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index dd78eda..24253f9 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -56,6 +56,7 @@ use iced::widget::text_input::focus; use iced::widget::themer; use iced::window; use iced_fonts::BOOTSTRAP_FONT_BYTES; +use iced_fonts::LUCIDE_FONT_BYTES; use crate::model::UiViewEvent; use crate::ui::search_list::search_list; @@ -300,6 +301,7 @@ pub fn run(minimized: bool, scenario_runner_data: Option) { ..Default::default() }) .font(BOOTSTRAP_FONT_BYTES) + .font(LUCIDE_FONT_BYTES) .subscription(subscription) .theme(|state, _| state.theme.clone()) .run() diff --git a/rust/client/src/ui/widget/action_panel.rs b/rust/client/src/ui/widget/action_panel.rs index 2b22860..3b65de4 100644 --- a/rust/client/src/ui/widget/action_panel.rs +++ b/rust/client/src/ui/widget/action_panel.rs @@ -285,7 +285,7 @@ pub fn render_shortcut<'a, T: 'a>(shortcut: &PhysicalShortcut) -> Element<'a, T> modifier: Option>, ) { if let Some(modifier) = modifier { - let modifier: Element<_> = container(modifier).themed(ContainerStyle::ActionShortcutModifier); + let modifier: Element<_> = container(modifier).center_y(Length::Fill).height(22).themed(ContainerStyle::ActionShortcutModifier); let modifier: Element<_> = container(modifier).themed(ContainerStyle::ActionShortcutModifiersInit); @@ -298,7 +298,7 @@ pub fn render_shortcut<'a, T: 'a>(shortcut: &PhysicalShortcut) -> Element<'a, T> apply_modifier(&mut result, shift_modifier_text); apply_modifier(&mut result, alt_modifier_text); - let key_name: Element<_> = container(key_name).themed(ContainerStyle::ActionShortcutModifier); + let key_name: Element<_> = container(key_name).center_y(Length::Fill).height(22).themed(ContainerStyle::ActionShortcutModifier); result.push(key_name); diff --git a/rust/common_ui/src/lib.rs b/rust/common_ui/src/lib.rs index a3329d9..825e4cf 100644 --- a/rust/common_ui/src/lib.rs +++ b/rust/common_ui/src/lib.rs @@ -6,10 +6,11 @@ use iced::Pixels; use iced::border::Radius; use iced::keyboard::Modifiers; use iced::widget::text; -use iced_fonts::bootstrap::arrow_return_left; -use iced_fonts::bootstrap::command; -use iced_fonts::bootstrap::option; -use iced_fonts::bootstrap::shift; +use iced_fonts::lucide::arrow_big_up; +use iced_fonts::lucide::chevron_up; +use iced_fonts::lucide::command; +use iced_fonts::lucide::corner_down_left; +use iced_fonts::lucide::option; pub fn padding( top: impl Into, @@ -50,9 +51,9 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( let (key_name, show_shift) = match shortcut.physical_key { PhysicalKey::Enter => { let key_name = if cfg!(target_os = "macos") { - arrow_return_left().into() + corner_down_left().size(14).into() } else { - text("Enter").into() + text("Enter").size(15).into() }; (key_name, shortcut.modifier_shift) @@ -60,7 +61,7 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( _ => { let (key_name, show_shift) = physical_key_name(&shortcut.physical_key, shortcut.modifier_shift); - let key_name: Element<_, _> = text(key_name).into(); + let key_name: Element<_, _> = text(key_name).size(15).into(); (key_name, show_shift) } @@ -68,9 +69,9 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( let alt_modifier_text = if shortcut.modifier_alt { if cfg!(target_os = "macos") { - Some(option().into()) + Some(option().size(15).into()) } else { - Some(text("Alt").into()) + Some(text("Alt").size(15).into()) } } else { None @@ -78,14 +79,15 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( let meta_modifier_text = if shortcut.modifier_meta { if cfg!(target_os = "macos") { - Some(command().into()) + Some(command().size(13).into()) } else if cfg!(target_os = "windows") { Some( text("Win") // is it possible to have shortcuts that use win? + .size(15) .into(), ) } else { - Some(text("Super").into()) + Some(text("Super").size(15).into()) } } else { None @@ -93,12 +95,9 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( let control_modifier_text = if shortcut.modifier_control { if cfg!(target_os = "macos") { - Some( - text("^") // TODO bootstrap doesn't have proper macos ctrl icon - .into(), - ) + Some(chevron_up().size(15).into()) } else { - Some(text("Ctrl").into()) + Some(text("Ctrl").size(15).into()) } } else { None @@ -106,9 +105,9 @@ pub fn shortcut_to_text<'a, Message, Theme: text::Catalog + 'a>( let shift_modifier_text = if show_shift && shortcut.modifier_shift { if cfg!(target_os = "macos") { - Some(shift().into()) + Some(arrow_big_up().size(17).into()) } else { - Some(text("Shift").into()) + Some(text("Shift").size(15).into()) } } else { None diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index d675d84..bb9397f 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use std::fs::File; use std::sync::Arc; -use std::time::Duration; use std::vec; use anyhow::Context; @@ -1236,7 +1235,7 @@ async fn show_linux_native_hud(display: String) -> anyhow::Result<()> { proxy.add_notification(notification_id, notification).await?; tokio::spawn(async move { - tokio::time::sleep(Duration::from_secs(2)).await; + tokio::time::sleep(std::time::Duration::from_secs(2)).await; let _ = proxy.remove_notification(notification_id).await; }); From de5166ccaed1343845a0b5ab9f9e44c80258ad26 Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Fri, 15 Aug 2025 18:23:24 +0200 Subject: [PATCH 87/91] Remove padding from content paragraph text --- rust/client/src/ui/theme/container.rs | 2 +- rust/client/src/ui/theme/mod.rs | 4 ---- rust/client/src/ui/widget/action_panel.rs | 10 ++++++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/rust/client/src/ui/theme/container.rs b/rust/client/src/ui/theme/container.rs index 359dd88..d4a9c12 100644 --- a/rust/client/src/ui/theme/container.rs +++ b/rust/client/src/ui/theme/container.rs @@ -325,7 +325,7 @@ impl<'a, Message: 'a> ThemableWidget<'a, Message> for Container<'a, Message, Gau ContainerStyle::ListItemSubtitle => self.padding(theme.list_item_subtitle.padding.to_iced()), ContainerStyle::ListItemTitle => self.padding(theme.list_item_title.padding.to_iced()), ContainerStyle::ListItemIcon => self.padding(theme.list_item_icon.padding.to_iced()), - ContainerStyle::ContentParagraph => self.padding(theme.content_paragraph.padding.to_iced()), + ContainerStyle::ContentParagraph => self, ContainerStyle::ContentHorizontalBreak => self.padding(theme.content_horizontal_break.padding.to_iced()), ContainerStyle::ContentCodeBlock => self.padding(theme.content_code_block.padding.to_iced()), ContainerStyle::ContentCodeBlockText => { diff --git a/rust/client/src/ui/theme/mod.rs b/rust/client/src/ui/theme/mod.rs index 9bc9fcc..f151e01 100644 --- a/rust/client/src/ui/theme/mod.rs +++ b/rust/client/src/ui/theme/mod.rs @@ -45,7 +45,6 @@ pub struct GauntletComplexTheme { content_code_block_text: ThemeCode, content_horizontal_break: ThemePaddingOnly, content_image: ThemeImage, - content_paragraph: ThemePaddingOnly, detail_content: ThemePaddingOnly, detail_metadata: ThemePaddingOnly, empty_view_image: ThemePaddingSize, @@ -276,9 +275,6 @@ impl GauntletComplexTheme { list_item_title: ThemePaddingOnly { padding: padding_all(4.0), }, - content_paragraph: ThemePaddingOnly { - padding: padding_all(8.0), - }, content_code_block: ThemePaddingOnly { padding: padding_all(0.0), }, diff --git a/rust/client/src/ui/widget/action_panel.rs b/rust/client/src/ui/widget/action_panel.rs index 3b65de4..10997be 100644 --- a/rust/client/src/ui/widget/action_panel.rs +++ b/rust/client/src/ui/widget/action_panel.rs @@ -285,7 +285,10 @@ pub fn render_shortcut<'a, T: 'a>(shortcut: &PhysicalShortcut) -> Element<'a, T> modifier: Option>, ) { if let Some(modifier) = modifier { - let modifier: Element<_> = container(modifier).center_y(Length::Fill).height(22).themed(ContainerStyle::ActionShortcutModifier); + let modifier: Element<_> = container(modifier) + .center_y(Length::Fill) + .height(22) + .themed(ContainerStyle::ActionShortcutModifier); let modifier: Element<_> = container(modifier).themed(ContainerStyle::ActionShortcutModifiersInit); @@ -298,7 +301,10 @@ pub fn render_shortcut<'a, T: 'a>(shortcut: &PhysicalShortcut) -> Element<'a, T> apply_modifier(&mut result, shift_modifier_text); apply_modifier(&mut result, alt_modifier_text); - let key_name: Element<_> = container(key_name).center_y(Length::Fill).height(22).themed(ContainerStyle::ActionShortcutModifier); + let key_name: Element<_> = container(key_name) + .center_y(Length::Fill) + .height(22) + .themed(ContainerStyle::ActionShortcutModifier); result.push(key_name); From 1e8bd2eca924abca9483fceef6afd85cdc2276ee Mon Sep 17 00:00:00 2001 From: Exidex <16986685+exidex@users.noreply.github.com> Date: Sat, 16 Aug 2025 17:37:07 +0200 Subject: [PATCH 88/91] Update CHANGELOG.md --- CHANGELOG.md | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8422b89..606899b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,56 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project doesn't adhere to Semantic Versioning, see [Versioning](./README.md#versioning) +and this project doesn't adhere to Semantic Versioning, see [Versioning](https://gauntlet.sh/docs/information/versioning) For changes in `@project-gauntlet/tools` see [separate CHANGELOG.md](https://github.com/project-gauntlet/tools/blob/main/CHANGELOG.md) ## [Unreleased] +### General + +- When opening `Opened windows` view second item is now focused by default + - Because the window ordering is "most recently focused on the top", second can be considered as an "alternative" application that was focused before the last one +- Implemented native hud notifications on Linux + - Enabled by default + - `linux.native_hud` boolean configuration option is available to disable this +- Restricted JavaScript runtime heap size to 50 MB per plugin + +### Plugins +- It is now possible to programmatically control which item in grid/list is focused + - `` and `` now have new property `focusedItemId` + - If `focusedItemId` property is `undefined` the focus is uncontrolled + - if `focusedItemId` property is `null` the focus is controlled and unset + - if `focusedItemId` property is `string` the focus is controlled and set to item with specified `id` +- Refine nullability of event function arguments on React components + - **BREAKING CHANGE**: Following function properties now return `null` as an argument instead of `undefined` + - ``'s `onAction` + - ``'s `onItemFocusChange` + - ``'s `onItemFocusChange` + - For following function property arguments `undefined` was removed from type signature + - ``'s `onChange` + - ``'s `onChange` + - ``'s `onChange` + - `