diff --git a/.gitignore b/.gitignore index 0c66e71..ee95f35 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -frontend/node_modules/ -frontend/dist/ -__pycache__/ +dist/ +target/ +node_modules/ + diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5389d56 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1740 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ashpd" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe7e0dd0ac5a401dc116ed9f9119cf9decc625600474cb41f0fc0a0050abc9a" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand", + "serde", + "serde_repr", + "url", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-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-process" +version = "2.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a07789659a4d385b79b18b9127fc27e1a59e1e89117c78c5ea3b806f016374" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[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 = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "cc" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +dependencies = [ + "shlex", +] + +[[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.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[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 = "cpufeatures" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[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 = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[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", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "headers" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" +dependencies = [ + "base64", + "bytes", + "headers-core", + "http 0.2.12", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http 0.2.12", +] + +[[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 = "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.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +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 = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "libc" +version = "0.2.156" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[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 = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[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 = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "multer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 0.2.12", + "httparse", + "log", + "memchr", + "mime", + "spin", + "version_check", +] + +[[package]] +name = "next" +version = "0.1.0" +dependencies = [ + "ashpd", + "bytes", + "futures-util", + "num-derive", + "num-traits", + "tokio", + "warp", + "zbus", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[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", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[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", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "polling" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[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 = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[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 = "serde" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.208" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +dependencies = [ + "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", +] + +[[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 = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "2.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys 0.52.0", +] + +[[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", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[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.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "warp" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "headers", + "http 0.2.12", + "hyper", + "log", + "mime", + "mime_guess", + "multer", + "percent-encoding", + "pin-project", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-tungstenite", + "tokio-util", + "tower-service", + "tracing", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[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-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[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.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[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", +] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9eb95b4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "next" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1", features = ["full"] } +warp = "0.3" +ashpd = "0.9" +futures-util = { version = "0.3", default-features = false, features = ["sink"] } +bytes = "1.7" +num-traits = "0.2" +num-derive = "0.4" +zbus = { version = "4", default-features = false, features = ["tokio"] } diff --git a/InputController.py b/InputController.py deleted file mode 100644 index f471549..0000000 --- a/InputController.py +++ /dev/null @@ -1,80 +0,0 @@ -from pynput.mouse import Controller as MouseController -from pynput.keyboard import Controller as KeyboardController - -import constants as c -from MessageParser import MessageParser - -class InputController(): - def __init__(self): - self.mouse_controller = MouseController() - self.keyboard_controller = KeyboardController() - self.parser = MessageParser() - - # Keyboard key press - self.parser.add_handler("k", { - "key": "str" - }) - # Relative mouse movement - self.parser.add_handler("r", { - "x": "float", - "y": "float" - }) - # Mouse relative scroll - self.parser.add_handler("s", { - "x": "float", - "y": "float" - }) - # Mouse button down - self.parser.add_handler("d", { - "button": "int" - }) - # Mouse button up - self.parser.add_handler("u", { - "button": "int" - }) - # Mouse button click - self.parser.add_handler("c", { - "button": "int" - }) - def button_code_to_object(self, button_code: int): - # HACK - obj = None - try: - obj = c.button_code_lookup[button_code] - except IndexError: - return c.button_code_lookup[0] - return obj - def deserialize_key(self, key: str): - obj = None - try: - obj = c.keyboard_lookup[key] - except KeyError: - if len(key) != 1: - return None - return key - return obj - def process_message(self, message: str) -> bool: - code, args = self.parser.parse(message) - - if code == None: - print("error while parsing message:", args) - return False - elif code == "r": - self.mouse_controller.move(args["x"], args["y"]) - elif code == "d": - self.mouse_controller.press(self.button_code_to_object(args["button"])) - elif code == "u": - self.mouse_controller.release(self.button_code_to_object(args["button"])) - elif code == "c": - self.mouse_controller.click(self.button_code_to_object(args["button"])) - elif code == "s": - self.mouse_controller.scroll(args["x"], args["y"]) - elif code == "k": - key = self.deserialize_key(args["key"]) - if key: - self.keyboard_controller.tap(key) - else: - print("got invalid code from parser (is this a bug with the MessageParser?)") - return False - - return True diff --git a/LICENSE b/LICENSE index 117a364..af08f12 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 hippoz +Copyright (c) 2024 hippoz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/MessageParser.py b/MessageParser.py deleted file mode 100644 index 5ef79ec..0000000 --- a/MessageParser.py +++ /dev/null @@ -1,40 +0,0 @@ -class MessageParser(): - def __init__(self): - self.handlers = {} - def add_handler(self, code: str, args={}): - self.handlers[code] = { - "args": args - } - def parse(self, message: str): - if len(message) < 1: - return None, "Message is empty" - message_code = message[0] - if message_code not in self.handlers: - return None, "Message code is not handled" - message_arguments = message[1:].split(";") - handler = self.handlers[message_code] - decoded_arguments = {} - if len(handler["args"]) != len(message_arguments): - return None, "Got message with invalid argument count" - for i, argument_name in enumerate(handler["args"]): - argument_type = handler["args"][argument_name] - if argument_type == "int": - try: - decoded_arguments[argument_name] = int(message_arguments[i]) - except ValueError: - return None, "Error parsing int argument for message (is it really an int?)" - elif argument_type == "float": - try: - decoded_arguments[argument_name] = float(message_arguments[i]) - except ValueError: - return None, "Error parsing float argument for message (is it really a float?)" - elif argument_type == "str": - decoded_arguments[argument_name] = message_arguments[i] - elif argument_type == "char": - if len(message_arguments[i]) != 1: - return None, "Error parsing char argument due to invalid size" - decoded_arguments[argument_name] = message_arguments[i] - else: - raise ValueError("parse(): Message handler references an invalid argument type") - - return message_code, decoded_arguments diff --git a/capybara.py b/capybara.py deleted file mode 100755 index 78ec090..0000000 --- a/capybara.py +++ /dev/null @@ -1,70 +0,0 @@ -import sys -from asyncio import wait_for, TimeoutError -from base64 import b64encode -from sanic import Sanic -from sanic.response import file, redirect, empty - -from InputController import InputController -from MessageParser import MessageParser - - -app = Sanic( - "capybara", - env_prefix="CAPYBARA_" -) -input_controller = InputController() -control_message_parser = MessageParser() - -# auth packet -control_message_parser.add_handler("0", { - "auth_string": "str" -}) - -app.static("app", "frontend/", resource_type="dir") - -@app.get("/") -async def home(req): - return redirect("/app/app.html") - -@app.get("/app") -async def app_route(req): - return redirect("/app/app.html") - -@app.websocket("/gateway") -async def gateway(req, ws): - # Before the client is able to send any data, we must await an authorization packet - try: - auth_payload = await wait_for(ws.recv(), timeout=5) - except TimeoutError: - await ws.close(code=4001, reason="Invalid auth packet") - return - - code, args = control_message_parser.parse(auth_payload) - if ( - not code or - code != "0" or - args["auth_string"] != app.ctx.EXPECTED_AUTH_STRING - ): # 0 is the code for the initial auth packet - await ws.close(code=4001, reason="Invalid auth packet") - return - - await ws.send("1") # send a single `1` to let the client know the server is accepting input packets - - while True: - input_controller.process_message(await ws.recv()) - - -def main(): - if not app.config.get("AUTH_PASSWORD"): - print( - "capybara: FATAL ERROR: Capybara is expecting the `CAPYBARA_AUTH_PASSWORD` environment variable to be set (to a password of your choice).\nYour users will use this password to authenticate to your server.\nEXITING...", - file=sys.stderr - ) - exit(1) - return - - app.ctx.EXPECTED_AUTH_STRING = "%auth%" + str(b64encode(app.config.AUTH_PASSWORD.encode("utf-8")), "utf-8") - app.run(host='0.0.0.0', port=4003, access_log=False) - -if __name__ == "__main__": - main() diff --git a/constants.py b/constants.py deleted file mode 100644 index debbfc3..0000000 --- a/constants.py +++ /dev/null @@ -1,13 +0,0 @@ -from pynput.keyboard import Key -from pynput.mouse import Button - -button_code_lookup = [ - Button.left, - Button.right -] - -keyboard_lookup = { - "{space}": Key.space, - "{ent}": Key.enter, - "{backspace}": Key.backspace -} diff --git a/frontend/App.js b/frontend/App.js deleted file mode 100644 index f0a6ffb..0000000 --- a/frontend/App.js +++ /dev/null @@ -1,109 +0,0 @@ -import Banner from "./Banner.js"; -import connection, { ConnectionEvent } from "./connection.js"; -import KeyboardController from "./Keyboard.js"; -import { setItem } from "./storage.js"; -import LoginPrompt from "./LoginPrompt.js"; -import TouchpadController from "./Touchpad.js"; - -class App { - constructor() { - this.connection = connection; - this.touchpad = new TouchpadController(this.connection); - this.keyboard = new KeyboardController(this.connection); - this.bannerComponent = new Banner(); - this.loginPromptComponent = new LoginPrompt(this.connection); - this.connectionHandlers = []; - } - - mountOn(mount, keepMountChildren=false) { - this.mountElement = mount; - - if (!keepMountChildren) { - while (mount.firstChild) { - mount.removeChild(mount.lastChild); - } - } - - this.connectionHandlers.push([ConnectionEvent.Closed, this.connection.subscribe(ConnectionEvent.Closed, (code) => { - this.unmountApp(); - if (code === 4001) { // 4001 - code for bad auth - this.transitionTo("login"); - } else { - this.transitionTo("reconnectingBanner"); - } - })]); - - this.connectionHandlers.push([ConnectionEvent.Ready, this.connection.subscribe(ConnectionEvent.Ready, () => { - this.transitionTo("app"); - })]); - } - - unmount() { - if (this.mountElement) { - this.unmountApp(); - this.unmountBannerComponent(); - this.unmountLoginComponent(); - } - - this.connectionHandlers.forEach(([ event, handler ]) => { - this.connection.unsubscribe(event, handler); - }); - } - - transitionTo(type) { - switch (type) { - case "login": - this.unmountBannerComponent(); - this.unmountApp(); - this.mountLoginComponent(); - break; - case "app": - this.unmountBannerComponent(); - this.unmountLoginComponent(); - this.mountApp(); - break; - case "reconnectingBanner": - this.unmountApp(); - this.unmountLoginComponent(); - this.mountBannerComponent("Connecting...", "Looks like you've lost connection. We're trying to reconnect you."); - break; - default: - throw new Error(`transitionTo type ${type} is invalid`); - } - } - - mountBannerComponent(title, text) { - this.bannerComponent.mountOn(this.mountElement); - this.bannerComponent.updateTitle(title); - this.bannerComponent.updateText(text); - } - - unmountBannerComponent() { - this.bannerComponent.unmount(); - } - - mountLoginComponent() { - this.loginPromptComponent.mountOn(this.mountElement); - this.loginPromptComponent.onPasswordSubmitted = (p) => { - setItem("auth:token", p); - this.connection.connect(); - }; - } - - unmountLoginComponent() { - if (this.loginPromptComponent) - this.loginPromptComponent.unmount(); - } - - mountApp() { - this.touchpad.mountOn(this.mountElement); - this.keyboard.mountOn(this.mountElement); - } - - unmountApp() { - this.touchpad.unmount(); - this.keyboard.unmount(); - } -} - -export default App; diff --git a/frontend/Banner.js b/frontend/Banner.js deleted file mode 100644 index 9443f6a..0000000 --- a/frontend/Banner.js +++ /dev/null @@ -1,42 +0,0 @@ -class Banner { - constructor() { - this.element = null; - - this.title = ""; - this.text = ""; - } - - updateText(newText) { - this.text = newText; - this.element.querySelector("#banner-text").innerText = this.text; - } - - updateTitle(newTitle) { - this.title = newTitle; - this.element.querySelector("#banner-title").innerText = this.title; - } - - mountOn(target) { - if (this.element) - return; // Already mounted - - this.element = document.createRange().createContextualFragment(` -
- - -
- `).children[0]; - - target.appendChild(this.element); - } - - unmount() { - if (!this.element) - return; // Already unmounted - - this.element.parentElement.removeChild(this.element); - this.element = null; - } -} - -export default Banner; diff --git a/frontend/Keyboard.js b/frontend/Keyboard.js deleted file mode 100644 index 313a16d..0000000 --- a/frontend/Keyboard.js +++ /dev/null @@ -1,126 +0,0 @@ -class KeyboardController { - constructor(connection) { - this.connection = connection; - this.container = null; - - this.layouts = { - default: [ - ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], - ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"], - ["a", "s", "d", "f", "g", "h", "j", "k", "l"], - ["🄰", "z", "x", "c", "v", "b", "n", "m", ".", "⌦"], - ["🔢", " ", "↵"] - ], - uppercase: [ - ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"], - ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"], - ["A", "S", "D", "F", "G", "H", "J", "K", "L"], - ["🅰", "Z", "X", "C", "V", "B", "N", "M", ",", "⌦"], - ["🔢", " ", "↵"] - ], - symbols: [ - ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"], - ["[", "]", "{", "}", ";", ":", "'", "\"", ",", "<"], - [".", ">", "-", "_", "=", "+", "/", "?", "\\"], - ["🄰", "|", "`", "~", "⌦"], - ["🔤", " ", "↵"] - ] - } - } - - _sendKeyPress(l) { - this.connection.sendMessage("k", [l]); - } - - makeKeyboardForLayout(layout) { - queueMicrotask(() => { - while (this.container.firstChild) { - this.container.removeChild(this.container.lastChild); - } - - const rowsParent = document.createElement("div"); - rowsParent.classList.add("Keyboard-rows"); - - const rowsFragment = document.createDocumentFragment(); - - for (let row = 0; row < layout.length; row++) { - const rowElement = document.createElement("div"); - rowElement.classList.add("Keyboard-row"); - for (let col = 0; col < layout[row].length; col++) { - const key = layout[row][col]; - - const colElement = document.createElement("button"); - colElement.classList.add("Keyboard-button"); - colElement.innerText = key; - colElement.addEventListener("click", () => { - this.handleKeypress(key); - }); - - rowElement.appendChild(colElement); - } - rowsFragment.appendChild(rowElement); - } - - rowsParent.appendChild(rowsFragment); - this.container.appendChild(rowsParent); - }); - } - - handleKeypress(key) { - switch (key) { - case "🄰": { - this.makeKeyboardForLayout(this.layouts.uppercase); - break; - } - case "🅰": { - this.makeKeyboardForLayout(this.layouts.default); - break; - } - case "🔢": { - this.makeKeyboardForLayout(this.layouts.symbols); - break; - } - case "🔤": { - this.makeKeyboardForLayout(this.layouts.default); - break; - } - case " ": { - this._sendKeyPress("{space}"); - break; - } - case "⌦": { - this._sendKeyPress("{backspace}"); - break; - } - case "↵": { - this._sendKeyPress("{ent}"); - break; - } - default: { - this._sendKeyPress(key); - break; - } - } - } - - mountOn(element) { - if (this.container) - return; // Already mounted; - - this.container = document.createElement("div"); - this.container.classList.add("Keyboard"); - element.appendChild(this.container); - - this.makeKeyboardForLayout(this.layouts.default); - } - - unmount() { - if (!this.container) - return; // Not mounted - don't do anything - - this.container.parentElement.removeChild(this.container); - this.container = null; - } -} - -export default KeyboardController; \ No newline at end of file diff --git a/frontend/LoginPrompt.js b/frontend/LoginPrompt.js deleted file mode 100644 index 4f1ef78..0000000 --- a/frontend/LoginPrompt.js +++ /dev/null @@ -1,35 +0,0 @@ -class LoginPrompt { - constructor() { - this.element = null; - } - - mountOn(target) { - if (this.element) - return; // Already mounted - - this.element = document.createRange().createContextualFragment(` -
-

Login

- - -
- `).children[0]; - - this.element.querySelector("#continue-button").addEventListener("click", () => { - if (this.onPasswordSubmitted) - this.onPasswordSubmitted(this.element.querySelector("#code-input").value); - }); - - target.appendChild(this.element); - } - - unmount() { - if (!this.element) - return; // Already unmounted - - this.element.parentElement.removeChild(this.element); - this.element = null; - } -} - -export default LoginPrompt; diff --git a/frontend/Touchpad.js b/frontend/Touchpad.js deleted file mode 100644 index d26dfeb..0000000 --- a/frontend/Touchpad.js +++ /dev/null @@ -1,217 +0,0 @@ -const HOLDING_THRESHOLD_MS = 300; -const SCROLL_X_DAMPENING = 0.02; -const SCROLL_Y_DAMPENING = 0.09; -const MOVE_X_MULTIPLIER = 1.64; -const MOVE_Y_MULTIPLIER = 1.64; - -class TouchpadController { - constructor(connection) { - this.currentMoveX = 0; - this.currentMoveY = 0; - this.lastMoveX = 0; - this.lastMoveY = 0; - this.shouldResetLastMove = false; - this.isInHoldingMode = false; - this.ongoingTouches = {}; - this.holdModeTimeout = null; - this.touchpadDiv = null; - - this.connection = connection; - } - - getButtonCode(button) { - let buttonCode = 0; - if (button === "right") buttonCode = 1; - return buttonCode; - } - - // takes a gesture name and a touch list - // adds the gesture name to the touch and returns false if that touch already had that gesture - _gesture(gestureName, touchList) { - for (let i = 0; i < touchList.length; i++) { - const touch = this.ongoingTouches[touchList[i].identifier]; - if (touch.gestured.includes(gestureName)) return false; - touch.gestured.push(gestureName); - } - return true; - } - - _sendRelativeMouseMovement(dx, dy) { - if (dx === 0 && dy === 0) - return false; - this.connection.sendMessage("r", [dx, dy]); - return true; - } - - _sendRelativeMouseScroll(dx, dy) { - if (dx === 0 && dy === 0) - return false; - this.connection.sendMessage("s", [dx, dy]); - return true; - } - - _sendMouseButtonDown(button="left") { - this.connection.sendMessage("d", [this.getButtonCode(button)]); - } - - _sendMouseButtonUp(button="left") { - this.connection.sendMessage("u", [this.getButtonCode(button)]); - } - - _sendSingleClick(button="left") { - this.connection.sendMessage("c", [this.getButtonCode(button)]); - } - - mountOn(element) { - if (this.touchpadDiv) - return; // Already mounted - - this.touchpadDiv = document.createElement("div"); - this.touchpadDiv.classList.add("Touchpad"); - - this.touchpadDiv.addEventListener("touchmove", this.onTouchMove.bind(this)); - this.touchpadDiv.addEventListener("touchend", this.onTouchEnd.bind(this)); - this.touchpadDiv.addEventListener("touchstart", this.onTouchStart.bind(this)); - - element.appendChild(this.touchpadDiv); - } - - unmount() { - if (!this.touchpadDiv) - return; // Not mounted - don't do anything - - this.touchpadDiv.removeEventListener("touchmove", this.onTouchMove.bind(this)); - this.touchpadDiv.removeEventListener("touchend", this.onTouchEnd.bind(this)); - this.touchpadDiv.removeEventListener("touchstart", this.onTouchStart.bind(this)); - } - - onTouchMove(event) { - const touches = event.changedTouches; - - const targetTouch = touches[0]; - - this.currentMoveX = targetTouch.pageX; - this.currentMoveY = targetTouch.pageY; - // When ending a touch and starting a new one in another part of the touchpad, - // the cursor "rubber bands" to that position. - // To solve this, the "last move" parameters are reset when a touch is ended - // (see onTouchEnd()) - if (this.shouldResetLastMove) { - this.shouldResetLastMove = false; - this.lastMoveX = this.currentMoveX; - this.lastMoveY = this.currentMoveY; - } - const deltaX = this.currentMoveX - this.lastMoveX; - const deltaY = this.currentMoveY - this.lastMoveY; - this.lastMoveX = this.currentMoveX; - this.lastMoveY = this.currentMoveY; - // if two touches moved at the same time, assume scrolling intent - - // if _sendRelativeMouseScroll or _sendRelativeMouseMovement return true, it means that the delta values are non-zero and a packet has been sent to the server - // in that case, the touches will be marked as moved - let shouldMarkTouchesAsMoved = false; - if (touches.length === 2) { - if ( - this._sendRelativeMouseScroll(deltaX * SCROLL_X_DAMPENING, deltaY * SCROLL_Y_DAMPENING) - ) { - shouldMarkTouchesAsMoved = true; - } - } else if (touches.length === 3 && this._gesture("GESTURE_RIGHT_CLICK", touches)) { - shouldMarkTouchesAsMoved = true; - this._sendSingleClick("right"); - } else { - if ( - this._sendRelativeMouseMovement(deltaX * MOVE_X_MULTIPLIER, deltaY * MOVE_Y_MULTIPLIER) - ) { - shouldMarkTouchesAsMoved = true; - } - } - if (shouldMarkTouchesAsMoved) { - for (let i = 0; i < touches.length; i++) { - const touch = this.ongoingTouches[touches[i].identifier]; - touch.hasMoved = true; - - if (touch.indicatorElement) { - touch.indicatorElement.style.top = `${targetTouch.pageY}px`; - touch.indicatorElement.style.left = `${targetTouch.pageX}px`; - } - } - } - } - - onTouchEnd(event) { - const changedTouches = event.changedTouches; - this.shouldResetLastMove = true; - - if (changedTouches.length === 1) { - // This is a single tap - left click - if (!this.ongoingTouches[changedTouches[0].identifier].hasMoved) { - this._sendSingleClick("left"); - - // We were in "holding mode" and now that touch event has ended, - // thus we have to stop holding the left click button - } else if (this.isInHoldingMode) { - this._sendMouseButtonUp("left"); - this.isInHoldingMode = false; - } - } - - // remove all ended touches - for (let i = 0; i < changedTouches.length; i++) { - const touch = changedTouches[i]; - const knownTouch = this.ongoingTouches[changedTouches[0].identifier]; - - if (knownTouch && knownTouch.indicatorElement) { - this.touchpadDiv.removeChild(knownTouch.indicatorElement); - knownTouch.indicatorElement = null; - delete knownTouch.indicatorElement; - } - - this.ongoingTouches[touch.identifier] = null; - delete this.ongoingTouches[touch.identifier]; - } - } - - onTouchStart(event) { - const changedTouches = event.changedTouches; - - // Clear the hold mode time out if another touch begins - if (this.holdModeTimeout) - clearTimeout(this.holdModeTimeout); - - // If the touch is still unmoved and held for a certain amount of time, - // we will enter "holding mode" which keeps the mouse button held while dragging - // allowing you to, for example, select text or drag a scrollbar - if (changedTouches.length === 1) { - const targetTouch = changedTouches[0]; - this.holdModeTimeout = setTimeout(() => { - if (this.ongoingTouches[targetTouch.identifier] && !this.ongoingTouches[targetTouch.identifier].hasMoved) { - this.isInHoldingMode = true; - this._sendMouseButtonDown("left"); - } - }, HOLDING_THRESHOLD_MS); - } - - for (let i = 0; i < changedTouches.length; i++) { - const touch = changedTouches[i]; - let indicatorElement = this.ongoingTouches[touch.identifier] ? this.ongoingTouches[touch.identifier].indicatorElement : null; - if (!indicatorElement) { - indicatorElement = document.createElement("div"); - indicatorElement.classList.add("Touchpad-touch-indicator"); - indicatorElement.style.top = `${touch.clientY}px`; - indicatorElement.style.left = `${touch.clientX}px`; - this.touchpadDiv.appendChild(indicatorElement); - } - this.ongoingTouches[touch.identifier] = { - identifier: touch.identifier, - clientX: touch.clientX, - clientY: touch.clientY, - hasMoved: false, - gestured: [], - indicatorElement - }; - } - } -} - -export default TouchpadController; \ No newline at end of file diff --git a/frontend/app.html b/frontend/app.html deleted file mode 100644 index 3eaae33..0000000 --- a/frontend/app.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - Capybara - - - - - -
-

Loading...

-
- - \ No newline at end of file diff --git a/frontend/connection.js b/frontend/connection.js deleted file mode 100644 index 68cd394..0000000 --- a/frontend/connection.js +++ /dev/null @@ -1,101 +0,0 @@ -import logger from "./logger.js"; -import { getItem } from "./storage.js"; - -export const ConnectionEvent = { - Open: 0, - Closed: 1, - Ready: 2, -}; - -export default { - ws: null, - log: logger(["Connection"], ["log"]).log, - messageLog: logger(["Connection", "Message"], ["log"]).log, - url: getItem("server:gatewayBase"), - isReady: false, - reconnectTimeout: 0, - handlers: new Map(), - - formatAuthString(password) { - return `%auth%${btoa(password)}`; - }, - - connect(password=getItem("auth:token")) { - this.ws = new WebSocket(this.url); - this.ws.onerror = (e) => this.log("Error", e); - this.ws.onopen = () => { - this.log("Open"); - this.dispatch(ConnectionEvent.Open, 1); - this.reconnectTimeout = 0; - this.log("Sending authentication packet"); - this.ws.send(`0${this.formatAuthString(password)}`); // send auth packet - }; - this.ws.onmessage = ({ data }) => { - if (data === "1") { - this.isReady = true; - this.dispatch(ConnectionEvent.Ready, 1); - this.log("Handshake complete"); - } - }; - this.ws.onclose = ({ code }) => { - this.dispatch(ConnectionEvent.Closed, code); - - if (code === 4001) {// code for bad auth - this.log("Closed due to bad auth - skipping reconnect"); - return; - } - this.isReady = false; - this.reconnectTimeout += 400; - if (this.reconnectTimeout >= 30000) - this.reconnectTimeout = 30000; - this.log(`Closed - will reconnect in ${this.reconnectTimeout}ms`); - setTimeout(() => this.connect(password), this.reconnectTimeout); - } - }, - - sendMessage(code, params=[]) { - let message = code; - for (let i = 0; i < params.length; i++) { - const param = params[i]; - if (i == params.length - 1) - message += param; - else - message += param + ";"; - } - - this.ws.send(message); - }, - - disconnect() { - this.ws.close(); - }, - - dispatch(event, data) { - const eventHandlers = this.handlers.get(event); - if (!eventHandlers) - return; - - eventHandlers.forEach(e => e(data)); - }, - - subscribe(event, handler) { - if (!this.handlers.get(event)) { - this.handlers.set(event, new Set()); - } - - this.handlers.get(event).add(handler); - return handler; // can later be used for unsubscribe() - }, - - unsubscribe(event, handler) { - const eventHandlers = this.handlers.get(event); - if (!eventHandlers) - return; - - eventHandlers.delete(handler); - - if (eventHandlers.size < 1) { - this.handlers.delete(event); - } - }, -}; diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..101078a --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,15 @@ + + + + + + + + Vite + Svelte + TS + + + + + + + diff --git a/frontend/logger.js b/frontend/logger.js deleted file mode 100644 index 43682dd..0000000 --- a/frontend/logger.js +++ /dev/null @@ -1,53 +0,0 @@ -const loggerOfType = (components, type='log') => (...args) => { - let str = '%c'; - const style = 'color: #5e81ac; font-weight: bold;'; - for (const i in components) { - const v = components[i]; - if (components[i+1] === undefined) { - str += `[${v}]`; - } else { - str += `[${v}] `; - } - } - switch (type) { - case 'log': { - console.log(str, style, ...args); - break; - } - - case 'error': { - console.error(str, style, ...args); - break; - } - - case 'warn': { - console.warn(str, style, ...args); - break; - } - - case 'genmsg': { - return str; - } - - default: { - return str; - } - } -}; - -function logger(components, types=['warn', 'error', 'log']) { - const loggerObj = {}; - - for (const type of types) { - loggerObj[type] = loggerOfType(components, type); - } - - return loggerObj; -} - - -export function domLog(message) { - document.body.appendChild(document.createTextNode(message)); -} - -export default logger; \ No newline at end of file diff --git a/frontend/main.css b/frontend/main.css deleted file mode 100644 index 00b5ea4..0000000 --- a/frontend/main.css +++ /dev/null @@ -1,167 +0,0 @@ -:root { - --body-bg-color: #2e2e2e; - --body-bg-color-accent: hsl(0, 0%, 25%); - --accent-bg-color: #d4d3d3; - --accent-color: #949494; - --grayed-text-color: #949494; - --button-accent-color: #3d3d3d; - --selected-bg-color: #2e2e2e; - --hover-bg-color: #4d4d4d; - --card-border-radius: 1em; - --button-border-radius: 0.5em; - --main-font-weight: 500; - - --fonts-regular: "Noto Sans", "Liberation Sans", sans-serif; -} - -* { - box-sizing: border-box; -} - -html, body { - font-weight: var(--main-font-weight); - font-family: var(--fonts-regular); - background-color: var(--body-bg-color); - color: var(--accent-bg-color); - overflow: hidden; - margin: 0; - padding: 0; - height: 100%; - width: 100%; -} - -body { - display: flex; - flex-direction: column; - align-items: center; - justify-content: flex-end; - background: var(--body-bg-color); - background-image: radial-gradient(var(--body-bg-color-accent) 1.4px, transparent 0); - background-size: 35px 35px; -} - -/* util */ - -.full-width { - width: 100%; -} - -/* Card */ - -.Keyboard, -.Card { - margin: 6px; - padding: 8px; - background: var(--accent-bg-color); - color: #000000; - border-radius: var(--card-border-radius); -} - -.Keyboard, -.Card-ui-bottom { - padding: 14px; - width: 100%; - max-width: 610px; - box-shadow: 0 0 28px 3px rgba(0, 0, 0, 0.40); - margin: 0; - border-radius: var(--card-border-radius) var(--card-border-radius) 0 0; - padding-top: 16px; -} - -.Card-centered-text { - text-align: center; -} - -/* button */ - -.button-default { - display: block; - padding: 12px; - margin: 4px; - border: none; - background-color: var(--accent-bg-color); - border-radius: var(--button-border-radius); - color: var(--button-accent-color); - min-width: 50px; - text-align: center; -} - -.button-default:hover:not(.button-selected) { - color: var(--accent-bg-color); - background-color: var(--hover-bg-color); -} - -.button-selected { - color: var(--accent-bg-color); - background-color: var(--selected-bg-color); -} - -/* input */ - -.input { - display: block; - box-sizing: border-box; - border: none; - outline: none; - border-radius: var(--button-border-radius); - margin: 4px; - padding: 12px; -} - -/* LoginPrompt */ - -.LoginPrompt { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - -/* Touchpad */ - -.Touchpad { - width: 100%; - height: 100%; -} - -.Touchpad-touch-indicator { - pointer-events: none; - position: absolute; - width: 28px; - height: 28px; - border-radius: 50%; - background-color: var(--accent-bg-color); -} - -/* Keyboard */ - -.keyboard-rows { - border-radius: 12px; - display: flex; - flex-direction: column; -} - -.Keyboard-row { - display: flex; - flex-direction: row; - flex-grow: 1; -} - -.Keyboard-button { - font-family: var(--fonts-regular); - font-weight: var(--main-font-weight); - padding: 8px; - background-color: var(--accent-bg-color); - color: #000000; - flex-grow: 1; - min-height: 50px; - width: 20px; - border: none; - display: flex; - align-items: center; - justify-content: center; -} - -.Keyboard-button:active { - background-color: var(--accent-color); -} diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..b7bf21f --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,1756 @@ +{ + "name": "capybara-frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "capybara-frontend", + "version": "0.0.0", + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.1.1", + "@tsconfig/svelte": "^5.0.4", + "svelte": "^4.2.18", + "svelte-check": "^3.8.5", + "tslib": "^2.6.3", + "typescript": "^5.5.3", + "vite": "^5.4.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz", + "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz", + "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz", + "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz", + "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz", + "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz", + "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz", + "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz", + "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz", + "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz", + "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz", + "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", + "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz", + "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz", + "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz", + "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz", + "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.1.tgz", + "integrity": "sha512-rimpFEAboBBHIlzISibg94iP09k/KYdHgVhJlcsTfn7KMBhc70jFX/GRWkRdFCc2fdnk+4+Bdfej23cMDnJS6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", + "debug": "^4.3.4", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.10", + "svelte-hmr": "^0.16.0", + "vitefu": "^0.2.5" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", + "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@tsconfig/svelte": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-5.0.4.tgz", + "integrity": "sha512-BV9NplVgLmSi4mwKzD8BD/NQ8erOY/nUE/GpgWe2ckx+wIQF5RyRirn/QsSSCPeulVpc3RA/iJt6DpfTIZps0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/pug": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", + "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "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/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/code-red": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", + "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1", + "acorn": "^8.10.0", + "estree-walker": "^3.0.3", + "periscopic": "^3.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/rollup": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", + "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.20.0", + "@rollup/rollup-android-arm64": "4.20.0", + "@rollup/rollup-darwin-arm64": "4.20.0", + "@rollup/rollup-darwin-x64": "4.20.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.20.0", + "@rollup/rollup-linux-arm-musleabihf": "4.20.0", + "@rollup/rollup-linux-arm64-gnu": "4.20.0", + "@rollup/rollup-linux-arm64-musl": "4.20.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.20.0", + "@rollup/rollup-linux-riscv64-gnu": "4.20.0", + "@rollup/rollup-linux-s390x-gnu": "4.20.0", + "@rollup/rollup-linux-x64-gnu": "4.20.0", + "@rollup/rollup-linux-x64-musl": "4.20.0", + "@rollup/rollup-win32-arm64-msvc": "4.20.0", + "@rollup/rollup-win32-ia32-msvc": "4.20.0", + "@rollup/rollup-win32-x64-msvc": "4.20.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sander": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", + "integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promise": "^3.1.2", + "graceful-fs": "^4.1.3", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2" + } + }, + "node_modules/sorcery": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.1.tgz", + "integrity": "sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.14", + "buffer-crc32": "^1.0.0", + "minimist": "^1.2.0", + "sander": "^0.5.0" + }, + "bin": { + "sorcery": "bin/sorcery" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/svelte": { + "version": "4.2.18", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.18.tgz", + "integrity": "sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/estree": "^1.0.1", + "acorn": "^8.9.0", + "aria-query": "^5.3.0", + "axobject-query": "^4.0.0", + "code-red": "^1.0.3", + "css-tree": "^2.3.1", + "estree-walker": "^3.0.3", + "is-reference": "^3.0.1", + "locate-character": "^3.0.0", + "magic-string": "^0.30.4", + "periscopic": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/svelte-check": { + "version": "3.8.5", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.5.tgz", + "integrity": "sha512-3OGGgr9+bJ/+1nbPgsvulkLC48xBsqsgtc8Wam281H4G9F5v3mYGa2bHRsPuwHC5brKl4AxJH95QF73kmfihGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "chokidar": "^3.4.1", + "picocolors": "^1.0.0", + "sade": "^1.7.4", + "svelte-preprocess": "^5.1.3", + "typescript": "^5.0.3" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "peerDependencies": { + "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" + } + }, + "node_modules/svelte-hmr": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", + "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.20 || ^14.13.1 || >= 16" + }, + "peerDependencies": { + "svelte": "^3.19.0 || ^4.0.0" + } + }, + "node_modules/svelte-preprocess": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz", + "integrity": "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@types/pug": "^2.0.6", + "detect-indent": "^6.1.0", + "magic-string": "^0.30.5", + "sorcery": "^0.11.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">= 16.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.10.2", + "coffeescript": "^2.5.1", + "less": "^3.11.3 || ^4.0.0", + "postcss": "^7 || ^8", + "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", + "pug": "^3.0.0", + "sass": "^1.26.8", + "stylus": "^0.55.0", + "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0", + "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "coffeescript": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "postcss-load-config": { + "optional": true + }, + "pug": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.1.tgz", + "integrity": "sha512-1oE6yuNXssjrZdblI9AfBbHCC41nnyoVoEZxQnID6yvQZAFBzxxkqoFLtHUMkYunL8hwOLEjgTuxpkRxvba3kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.41", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", + "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..b2acee6 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,21 @@ +{ + "name": "capybara-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-check --tsconfig ./tsconfig.json && tsc -p tsconfig.node.json" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.1.1", + "@tsconfig/svelte": "^5.0.4", + "svelte": "^4.2.18", + "svelte-check": "^3.8.5", + "tslib": "^2.6.3", + "typescript": "^5.5.3", + "vite": "^5.4.1" + } +} \ No newline at end of file diff --git a/frontend/src/app.css b/frontend/src/app.css new file mode 100644 index 0000000..bdd6c75 --- /dev/null +++ b/frontend/src/app.css @@ -0,0 +1,137 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + --space-unit: 1em; + --space-xxs: calc(0.25 * var(--space-unit)); + --space-xs: calc(0.5 * var(--space-unit)); + --space-xsplus: calc(0.65 * var(--space-unit)); + --space-sm: calc(0.75 * var(--space-unit)); + --space-smplus: calc(0.8 * var(--space-unit)); + --space-norm: var(--space-unit); + --space-normplus: calc(var(--space-unit) + var(--space-sm)); + --space-md: calc(1.25 * var(--space-unit)); + --space-lg: calc(2 * var(--space-unit)); + --space-xl: calc(3.25 * var(--space-unit)); + --space-xxl: calc(5.25 * var(--space-unit)); + + --radius-unit: 0.5em; + --radius-xxs: calc(0.25 * var(--radius-unit)); + --radius-xs: calc(0.5 * var(--radius-unit)); + --radius-xsplus: calc(0.65 * var(--radius-unit)); + --radius-sm: calc(0.75 * var(--radius-unit)); + --radius-norm: var(--radius-unit); + --radius-md: calc(1.25 * var(--radius-unit)); + --radius-mdplus: calc(1.5 * var(--radius-unit)); + --radius-lg: calc(2 * var(--radius-unit)); + --radius-xl: calc(3.25 * var(--radius-unit)); + --radius-xxl: calc(5.25 * var(--sradius-unit)); + + --h1: 1.802rem; + --h2: 1.602rem; + --h3: 1.424rem; + --h4: 1.266rem; + --h5: 1.125rem; + --h6: 0.889rem; + --p: 1rem; + + --viewportWidth: 100vw; + --viewportHeight: 100vh; + + + color-scheme: dark; + + --background-color-0: hsl(0, 0%, 6%); + --background-color-1: hsl(0, 0%, 10%); + --background-color-2: hsl(0, 0%, 13%); + --background-color-3: hsl(0, 0%, 15%); + --foreground-color-1: hsl(210, 100%, 100%); + --foreground-color-2: hsl(63, 10%, 82%); + --foreground-color-3: hsl(63, 2%, 60%); + --foreground-color-4: hsl(63, 2%, 49%); + + --purple-1: hsl(266, 63%, 64%); + --blue-1: hsl(200, 78%, 50%); + --green-1: hsl(140, 78%, 50%); + --yellow-1: hsl(50, 78%, 50%); + --red-1: hsl(2, 78%, 65%); + + --purple-2: hsl(266, 62%, 58%); + --purple-2-highlight: hsla(266, 62%, 58%, 0.1); + --blue-2: hsl(200, 78%, 45%); + --green-2: hsl(140, 78%, 40%); + --yellow-2: hsl(50, 78%, 60%); + --red-2: hsl(2, 78%, 60%); +} + +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; + padding: 0; +} + +body { + overflow: hidden; + font-size: 100%; + line-height: 26px; + width: var(--viewportWidth); + height: var(--viewportHeight); + background-color: var(--background-color-0); + color: var(--foreground-color-1); + + display: flex; + flex-direction: column; + padding: var(--space-sm); + gap: var(--space-sm); +} + + +/* buttons */ +.button { + color: var(--foreground-color-1); + background: none; + text-align: center; + border: none; + padding: 0.85em; + padding-top: 0.65em; + padding-bottom: 0.65em; + border-radius: 9999px; + font: inherit; + user-select: none; + font-weight: 600; +} + +.button:hover { + background-color: var(--background-color-2); +} + +.button-accent { + color: var(--colored-element-text-color); + background-color: var(--purple-1); +} + +.button-accent:hover { + background-color: var(--purple-2); +} + +.button-accent:disabled { + background-color: var(--purple-1); +} + +.button-danger { + color: var(--red-2); +} + +.button-danger:hover { + color: var(--red-1); +} diff --git a/frontend/src/components/App.svelte b/frontend/src/components/App.svelte new file mode 100644 index 0000000..5a804a8 --- /dev/null +++ b/frontend/src/components/App.svelte @@ -0,0 +1,33 @@ + + + +{#if !$gatewayReady} + Connecting... +{/if} + + +{#if showKeyboard} + +{/if} + diff --git a/frontend/src/components/ChipBar.d.ts b/frontend/src/components/ChipBar.d.ts new file mode 100644 index 0000000..4527d09 --- /dev/null +++ b/frontend/src/components/ChipBar.d.ts @@ -0,0 +1,8 @@ +declare interface ChipOption { + id: string; + hidden?: boolean; + selected?: boolean; + icon?: string; + text?: string; + handle?: (event: Event) => void; +}; diff --git a/frontend/src/components/ChipBar.svelte b/frontend/src/components/ChipBar.svelte new file mode 100644 index 0000000..966dc3f --- /dev/null +++ b/frontend/src/components/ChipBar.svelte @@ -0,0 +1,90 @@ + + + + + +
+ {#each options as option (option.id)} + {#if !option.hidden} + + {/if} + {/each} +
diff --git a/frontend/src/components/Keyboard.svelte b/frontend/src/components/Keyboard.svelte new file mode 100644 index 0000000..f3d924a --- /dev/null +++ b/frontend/src/components/Keyboard.svelte @@ -0,0 +1,87 @@ + + + + + +
+ {#each currentLayout as row} +
+ {#each row as key} + + {/each} +
+ {/each} +
diff --git a/frontend/src/components/Toast.svelte b/frontend/src/components/Toast.svelte new file mode 100644 index 0000000..c6444ed --- /dev/null +++ b/frontend/src/components/Toast.svelte @@ -0,0 +1,37 @@ + + + + +
diff --git a/frontend/src/components/TouchArea.svelte b/frontend/src/components/TouchArea.svelte new file mode 100644 index 0000000..ec0fcc6 --- /dev/null +++ b/frontend/src/components/TouchArea.svelte @@ -0,0 +1,153 @@ + + + + +
diff --git a/frontend/src/gateway.ts b/frontend/src/gateway.ts new file mode 100644 index 0000000..faa94e4 --- /dev/null +++ b/frontend/src/gateway.ts @@ -0,0 +1,196 @@ +import { get, writable } from "svelte/store"; + +export enum FrameType { + None = 0, + + // Client to server + Authenticate = 1, + // Server to client + Ready, + + // Client to server + PointerMotion = 10, + PointerAxis, + PointerAxisFinal, + PointerButtonPressed, + PointerButtonReleased, + PointerButtonTapped, + KeyboardButtonPressed, + KeyboardButtonReleased, + KeyboardButtonTapped +}; + +let ws: null | WebSocket = null; +const sizeToDataView = new Map(); +let reconnectTimeout: number | null = null; +const RECONNECT_TIME_MS = 800; + +export let gatewayReady = writable(false); + + +function isConnected(): boolean { + return ws !== null && ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING; +} + +function isReady() { + return isConnected() && get(gatewayReady); +} + +function wantsConnection(): boolean { + return document.visibilityState === "visible"; +} + +function clearReconnect() { + if (reconnectTimeout !== null) { + clearTimeout(reconnectTimeout); + reconnectTimeout = null; + } +} + +function getDataView(size: number): DataView { + const existing = sizeToDataView.get(size); + if (existing) return existing; + + const buffer = new ArrayBuffer(size); + const view = new DataView(buffer); + sizeToDataView.set(size, view); + + return view; +} + +function createFrame(type: FrameType, arg1: number, arg2: number): ArrayBuffer { + let view; + + switch (type) { + case FrameType.Authenticate: { + view = getDataView(1); + view.setUint8(0, FrameType.Authenticate); + break; + } + case FrameType.PointerMotion: { + view = getDataView(9); + view.setUint8(0, FrameType.PointerMotion); + view.setFloat32(1, arg1); + view.setFloat32(5, arg2); + break; + } + case FrameType.PointerAxis: { + view = getDataView(9); + view.setUint8(0, FrameType.PointerAxis); + view.setFloat32(1, arg1); + view.setFloat32(5, arg2); + break; + } + case FrameType.PointerAxisFinal: { + view = getDataView(9); + view.setUint8(0, FrameType.PointerAxisFinal); + view.setFloat32(1, arg1); + view.setFloat32(5, arg2); + break; + } + case FrameType.PointerButtonPressed: { + view = getDataView(5); + view.setUint8(0, FrameType.PointerButtonPressed); + view.setInt32(1, arg1); + break; + } + case FrameType.PointerButtonReleased: { + view = getDataView(5); + view.setUint8(0, FrameType.PointerButtonReleased); + view.setInt32(1, arg1); + break; + } + case FrameType.PointerButtonTapped: { + view = getDataView(5); + view.setUint8(0, FrameType.PointerButtonTapped); + view.setInt32(1, arg1); + break; + } + case FrameType.KeyboardButtonPressed: { + view = getDataView(5); + view.setUint8(0, FrameType.KeyboardButtonPressed); + view.setInt32(1, arg1); + break; + } + case FrameType.KeyboardButtonReleased: { + view = getDataView(5); + view.setUint8(0, FrameType.KeyboardButtonReleased); + view.setInt32(1, arg1); + break; + } + case FrameType.KeyboardButtonTapped: { + view = getDataView(5); + view.setUint8(0, FrameType.KeyboardButtonTapped); + view.setInt32(1, arg1); + break; + } + default: { + throw new Error(`bad FrameType: ${type}`); + } + } + + return view.buffer; +} + +export function sendFrame(type: FrameType, arg1: number = 0, arg2: number = 0) { + if (!isReady() || !ws) { + return; + } + + ws.send(createFrame(type, arg1, arg2)); +} + +export function connect() { + clearReconnect(); + ws = null; + gatewayReady.set(false); + + if (!wantsConnection()) return; + + ws = new WebSocket(`ws://${location.host}/gateway`); + ws.binaryType = "arraybuffer"; + ws.addEventListener("open", () => { + clearReconnect(); + + if (!ws) return; + + ws.send(createFrame(FrameType.Authenticate, 0, 0)); + }); + ws.addEventListener("message", (event: MessageEvent) => { + if (event.data && typeof event.data === "object" && event.data instanceof ArrayBuffer && event.data.byteLength) { + const view = new DataView(event.data); + const frameType = view.getUint8(0) as FrameType; + switch (frameType) { + case FrameType.Ready: { + gatewayReady.set(true); + break; + } + default: { + console.error(`got bad FrameType from server: ${frameType}`); + } + } + } + }); + ws.addEventListener("close", () => { + clearReconnect(); + ws = null; + gatewayReady.set(false); + + if (!wantsConnection()) return; + + reconnectTimeout = setTimeout(() => { + reconnectTimeout = null; + connect(); + }, RECONNECT_TIME_MS); + }); +} + +export function initVisibilityHandlers() { + document.addEventListener("visibilitychange", () => { + if (!isConnected() && wantsConnection()) { + queueMicrotask(connect); + } else if (ws && isConnected() && !wantsConnection()) { + ws.close(); + } + }); +} diff --git a/frontend/src/keyboard.ts b/frontend/src/keyboard.ts new file mode 100644 index 0000000..38f1642 --- /dev/null +++ b/frontend/src/keyboard.ts @@ -0,0 +1,237 @@ + +// XKB Keysyms. Taken from https://github.com/xkbcommon/libxkbcommon/blob/master/include/xkbcommon/xkbcommon-keysyms.h + +const KEY_BackSpace = 0xff08; /* U+0008 BACKSPACE */ +const KEY_Return = 0xff0d; /* U+000D CARRIAGE RETURN */ + +// Exported because they need special handling to change the keyboard layout. +export const KEY_Num_Lock = 0xff7f; +export const KEY_Caps_Lock = 0xffe5; /* Caps lock */ + +const KEY_space = 0x0020; /* U+0020 SPACE */ +const KEY_exclam = 0x0021; /* U+0021 EXCLAMATION MARK */ +const KEY_quotedbl = 0x0022; /* U+0022 QUOTATION MARK */ +const KEY_numbersign = 0x0023; /* U+0023 NUMBER SIGN */ +const KEY_dollar = 0x0024; /* U+0024 DOLLAR SIGN */ +const KEY_percent = 0x0025; /* U+0025 PERCENT SIGN */ +const KEY_ampersand = 0x0026; /* U+0026 AMPERSAND */ +const KEY_apostrophe = 0x0027; /* U+0027 APOSTROPHE */ +const KEY_parenleft = 0x0028; /* U+0028 LEFT PARENTHESIS */ +const KEY_parenright = 0x0029; /* U+0029 RIGHT PARENTHESIS */ +const KEY_plus = 0x002b; /* U+002B PLUS SIGN */ +const KEY_comma = 0x002c; /* U+002C COMMA */ +const KEY_minus = 0x002d; /* U+002D HYPHEN-MINUS */ +const KEY_period = 0x002e; /* U+002E FULL STOP */ +const KEY_slash = 0x002f; /* U+002F SOLIDUS */ +const KEY_0 = 0x0030; /* U+0030 DIGIT ZERO */ +const KEY_1 = 0x0031; /* U+0031 DIGIT ONE */ +const KEY_2 = 0x0032; /* U+0032 DIGIT TWO */ +const KEY_3 = 0x0033; /* U+0033 DIGIT THREE */ +const KEY_4 = 0x0034; /* U+0034 DIGIT FOUR */ +const KEY_5 = 0x0035; /* U+0035 DIGIT FIVE */ +const KEY_6 = 0x0036; /* U+0036 DIGIT SIX */ +const KEY_7 = 0x0037; /* U+0037 DIGIT SEVEN */ +const KEY_8 = 0x0038; /* U+0038 DIGIT EIGHT */ +const KEY_9 = 0x0039; /* U+0039 DIGIT NINE */ +const KEY_colon = 0x003a; /* U+003A COLON */ +const KEY_semicolon = 0x003b; /* U+003B SEMICOLON */ +const KEY_less = 0x003c; /* U+003C LESS-THAN SIGN */ +const KEY_equal = 0x003d; /* U+003D EQUALS SIGN */ +const KEY_greater = 0x003e; /* U+003E GREATER-THAN SIGN */ +const KEY_question = 0x003f; /* U+003F QUESTION MARK */ +const KEY_at = 0x0040; /* U+0040 COMMERCIAL AT */ +const KEY_A = 0x0041; /* U+0041 LATIN CAPITAL LETTER A */ +const KEY_B = 0x0042; /* U+0042 LATIN CAPITAL LETTER B */ +const KEY_C = 0x0043; /* U+0043 LATIN CAPITAL LETTER C */ +const KEY_D = 0x0044; /* U+0044 LATIN CAPITAL LETTER D */ +const KEY_E = 0x0045; /* U+0045 LATIN CAPITAL LETTER E */ +const KEY_F = 0x0046; /* U+0046 LATIN CAPITAL LETTER F */ +const KEY_G = 0x0047; /* U+0047 LATIN CAPITAL LETTER G */ +const KEY_H = 0x0048; /* U+0048 LATIN CAPITAL LETTER H */ +const KEY_I = 0x0049; /* U+0049 LATIN CAPITAL LETTER I */ +const KEY_J = 0x004a; /* U+004A LATIN CAPITAL LETTER J */ +const KEY_K = 0x004b; /* U+004B LATIN CAPITAL LETTER K */ +const KEY_L = 0x004c; /* U+004C LATIN CAPITAL LETTER L */ +const KEY_M = 0x004d; /* U+004D LATIN CAPITAL LETTER M */ +const KEY_N = 0x004e; /* U+004E LATIN CAPITAL LETTER N */ +const KEY_O = 0x004f; /* U+004F LATIN CAPITAL LETTER O */ +const KEY_P = 0x0050; /* U+0050 LATIN CAPITAL LETTER P */ +const KEY_Q = 0x0051; /* U+0051 LATIN CAPITAL LETTER Q */ +const KEY_R = 0x0052; /* U+0052 LATIN CAPITAL LETTER R */ +const KEY_S = 0x0053; /* U+0053 LATIN CAPITAL LETTER S */ +const KEY_T = 0x0054; /* U+0054 LATIN CAPITAL LETTER T */ +const KEY_U = 0x0055; /* U+0055 LATIN CAPITAL LETTER U */ +const KEY_V = 0x0056; /* U+0056 LATIN CAPITAL LETTER V */ +const KEY_W = 0x0057; /* U+0057 LATIN CAPITAL LETTER W */ +const KEY_X = 0x0058; /* U+0058 LATIN CAPITAL LETTER X */ +const KEY_Y = 0x0059; /* U+0059 LATIN CAPITAL LETTER Y */ +const KEY_Z = 0x005a; /* U+005A LATIN CAPITAL LETTER Z */ +const KEY_bracketleft = 0x005b; /* U+005B LEFT SQUARE BRACKET */ +const KEY_backslash = 0x005c; /* U+005C REVERSE SOLIDUS */ +const KEY_bracketright = 0x005d; /* U+005D RIGHT SQUARE BRACKET */ +const KEY_asciicircum = 0x005e; /* U+005E CIRCUMFLEX ACCENT */ +const KEY_underscore = 0x005f; /* U+005F LOW LINE */ +const KEY_grave = 0x0060; /* U+0060 GRAVE ACCENT */ +const KEY_quoteleft = 0x0060; /* deprecated */ +const KEY_a = 0x0061; /* U+0061 LATIN SMALL LETTER A */ +const KEY_b = 0x0062; /* U+0062 LATIN SMALL LETTER B */ +const KEY_c = 0x0063; /* U+0063 LATIN SMALL LETTER C */ +const KEY_d = 0x0064; /* U+0064 LATIN SMALL LETTER D */ +const KEY_e = 0x0065; /* U+0065 LATIN SMALL LETTER E */ +const KEY_f = 0x0066; /* U+0066 LATIN SMALL LETTER F */ +const KEY_g = 0x0067; /* U+0067 LATIN SMALL LETTER G */ +const KEY_h = 0x0068; /* U+0068 LATIN SMALL LETTER H */ +const KEY_i = 0x0069; /* U+0069 LATIN SMALL LETTER I */ +const KEY_j = 0x006a; /* U+006A LATIN SMALL LETTER J */ +const KEY_k = 0x006b; /* U+006B LATIN SMALL LETTER K */ +const KEY_l = 0x006c; /* U+006C LATIN SMALL LETTER L */ +const KEY_m = 0x006d; /* U+006D LATIN SMALL LETTER M */ +const KEY_n = 0x006e; /* U+006E LATIN SMALL LETTER N */ +const KEY_o = 0x006f; /* U+006F LATIN SMALL LETTER O */ +const KEY_p = 0x0070; /* U+0070 LATIN SMALL LETTER P */ +const KEY_q = 0x0071; /* U+0071 LATIN SMALL LETTER Q */ +const KEY_r = 0x0072; /* U+0072 LATIN SMALL LETTER R */ +const KEY_s = 0x0073; /* U+0073 LATIN SMALL LETTER S */ +const KEY_t = 0x0074; /* U+0074 LATIN SMALL LETTER T */ +const KEY_u = 0x0075; /* U+0075 LATIN SMALL LETTER U */ +const KEY_v = 0x0076; /* U+0076 LATIN SMALL LETTER V */ +const KEY_w = 0x0077; /* U+0077 LATIN SMALL LETTER W */ +const KEY_x = 0x0078; /* U+0078 LATIN SMALL LETTER X */ +const KEY_y = 0x0079; /* U+0079 LATIN SMALL LETTER Y */ +const KEY_z = 0x007a; /* U+007A LATIN SMALL LETTER Z */ +const KEY_braceleft = 0x007b; /* U+007B LEFT CURLY BRACKET */ +const KEY_bar = 0x007c; /* U+007C VERTICAL LINE */ +const KEY_braceright = 0x007d; /* U+007D RIGHT CURLY BRACKET */ +const KEY_asciitilde = 0x007e; /* U+007E TILDE */ +const KEY_multiply = 0x00d7; /* U+00D7 MULTIPLICATION SIGN */ + + + + +export const keyboardLayouts = { + "default": [ + ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], + ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"], + ["a", "s", "d", "f", "g", "h", "j", "k", "l"], + ["🄰", "z", "x", "c", "v", "b", "n", "m", ".", "⌦"], + ["🔢", " ", "↵"] + ], + "uppercase": [ + ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"], + ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"], + ["A", "S", "D", "F", "G", "H", "J", "K", "L"], + ["🅰", "Z", "X", "C", "V", "B", "N", "M", ",", "⌦"], + ["🔢", " ", "↵"] + ], + "symbols": [ + ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"], + ["[", "]", "{", "}", ";", ":", "'", "\"", ",", "<"], + [".", ">", "-", "_", "=", "+", "/", "?", "\\"], + ["🄰", "|", "`", "~", "⌦"], + ["🔤", " ", "↵"] + ], +}; + +export const charToLinuxEventCode = new Map(Object.entries({ + "1": KEY_1, + "2": KEY_2, + "3": KEY_3, + "4": KEY_4, + "5": KEY_5, + "6": KEY_6, + "7": KEY_7, + "8": KEY_8, + "9": KEY_9, + "0": KEY_0, + "q": KEY_q, + "w": KEY_w, + "e": KEY_e, + "r": KEY_r, + "t": KEY_t, + "y": KEY_y, + "u": KEY_u, + "i": KEY_i, + "o": KEY_o, + "p": KEY_p, + "a": KEY_a, + "s": KEY_s, + "d": KEY_d, + "f": KEY_f, + "g": KEY_g, + "h": KEY_h, + "j": KEY_j, + "k": KEY_k, + "l": KEY_l, + "🄰": KEY_Caps_Lock, + "z": KEY_z, + "x": KEY_x, + "c": KEY_c, + "v": KEY_v, + "b": KEY_b, + "n": KEY_n, + "m": KEY_m, + ".": KEY_period, + "⌦": KEY_BackSpace, + "🔢": KEY_Num_Lock, + "🔤": KEY_Num_Lock, + " ": KEY_space, + "↵": KEY_Return, + "!": KEY_exclam, + "@": KEY_at, + "#": KEY_numbersign, + "$": KEY_dollar, + "%": KEY_percent, + "^": KEY_asciicircum, + "&": KEY_ampersand, + "*": KEY_multiply, + "(": KEY_parenleft, + ")": KEY_parenright, + "Q": KEY_Q, + "W": KEY_W, + "E": KEY_E, + "R": KEY_R, + "T": KEY_T, + "Y": KEY_Y, + "U": KEY_U, + "I": KEY_I, + "O": KEY_O, + "P": KEY_P, + "A": KEY_A, + "S": KEY_S, + "D": KEY_D, + "F": KEY_F, + "G": KEY_G, + "H": KEY_H, + "J": KEY_J, + "K": KEY_K, + "L": KEY_L, + "🅰": KEY_Caps_Lock, + "Z": KEY_Z, + "X": KEY_X, + "C": KEY_C, + "V": KEY_V, + "B": KEY_B, + "N": KEY_N, + "M": KEY_M, + ",": KEY_comma, + "[": KEY_bracketright, + "]": KEY_bracketleft, + "{": KEY_braceright, + "}": KEY_braceleft, + ";": KEY_semicolon, + ":": KEY_colon, + "'": KEY_apostrophe, + "\"": KEY_quotedbl, + "<": KEY_less, + ">": KEY_greater, + "-": KEY_minus, + "_": KEY_underscore, + "=": KEY_equal, + "+": KEY_plus, + "/": KEY_slash, + "?": KEY_question, + "\\": KEY_backslash, + "|": KEY_bar, + "`": KEY_grave, + "~": KEY_asciitilde, +})); diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..fce39b5 --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,16 @@ +import './app.css'; +import App from './components/App.svelte'; +import { connect, initVisibilityHandlers } from './gateway'; +import { initViewportExtents } from './responsive'; + + +initViewportExtents(); +connect(); +initVisibilityHandlers(); + +const app = new App({ + target: document.body +}); + + +export default app; diff --git a/frontend/src/responsive.ts b/frontend/src/responsive.ts new file mode 100644 index 0000000..dc790eb --- /dev/null +++ b/frontend/src/responsive.ts @@ -0,0 +1,15 @@ +export const initViewportExtents = () => { + const root = document.querySelector(':root'); + + if (CSS.supports("(width: 1dvw)") && root) { + root.style.setProperty("--viewportWidth", "100dvw"); + root.style.setProperty("--viewportHeight", "100dvh"); + } else if (root) { + const updateUnits = () => { + root.style.setProperty("--viewportWidth", `${window.innerWidth}px`); + root.style.setProperty("--viewportHeight", `${window.innerHeight}px`); + }; + window.addEventListener("resize", updateUnits); + updateUnits(); + } +}; diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..4078e74 --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/frontend/storage.js b/frontend/storage.js deleted file mode 100644 index 9bb86cf..0000000 --- a/frontend/storage.js +++ /dev/null @@ -1,47 +0,0 @@ -const defaults = { - "server:gatewayBase": `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}/gateway`, - "auth:token": "", -}; -const store = new Map(Object.entries(defaults)); -const persistentProvider = localStorage; -let didCacheProvider = false; - -export function setItem(key, value) { - store.set(key, value); - if (persistentProvider) { - persistentProvider.setItem(key, typeof value === "string" ? value : JSON.stringify(value)); - } -} - -export function getItem(key) { - if (!didCacheProvider) { - init(); - } - return store.get(key); -} - -export function removeItem(key) { - store.delete(key); - if (persistentProvider) { - persistentProvider.removeItem(key); - } -} - -export function init() { - if (!persistentProvider) - return; - - store.forEach((defaultValue, key) => { - const override = persistentProvider.getItem(key); - if (override !== null) { - try { - store.set(key, typeof defaultValue === "string" ? override : JSON.parse(override)); - } catch (o_O) { - console.warn("[Storage]", `init(): An exception was thrown while parsing the value of key "${key}" from persistentProvider. The key "${key}" will be removed from persistentProvider.`, o_O); - persistentProvider.removeItem(key); - } - } - }); - - didCacheProvider = true; -} diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js new file mode 100644 index 0000000..61eb947 --- /dev/null +++ b/frontend/svelte.config.js @@ -0,0 +1,7 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' + +export default { + // Consult https://svelte.dev/docs#compile-time-svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess(), +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..5b2c1a8 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,29 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "resolveJsonModule": true, + /** + * Typecheck JS in `.svelte` and `.js` files by default. + * Disable checkJs if you'd like to use dynamic types in JS. + * Note that setting allowJs false does not prevent the use + * of JS in `.svelte` files. + */ + "allowJs": true, + "checkJs": true, + "isolatedModules": true, + "moduleDetection": "force" + }, + "include": [ + "src/**/*.ts", + "src/**/*.js", + "src/**/*.svelte" + ], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..396d96d --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noEmit": true + }, + "include": [ + "vite.config.ts" + ] +} \ No newline at end of file diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..5f1c8cb --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import { svelte } from '@sveltejs/vite-plugin-svelte' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [svelte()], +}) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 4e2a732..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pynput -sanic diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f10133b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,274 @@ +use std::{process::ExitCode, sync::Arc}; +use ashpd::{ + desktop::{ + remote_desktop::{DeviceType, KeyState, RemoteDesktop}, + PersistMode, Session, + }, + WindowIdentifier, +}; +use bytes::{Buf, BufMut, BytesMut}; +use warp::{ + filters::ws::Message, ws::WebSocket, Filter +}; +use futures_util::{SinkExt, StreamExt}; +use num_traits::FromPrimitive; +use num_derive::FromPrimitive; +use zbus::{proxy, zvariant::{DeserializeDict, OwnedValue, SerializeDict, Type, Value}, Connection, Result}; + + +#[repr(u8)] +#[derive(FromPrimitive, Debug)] +enum FrameType { + None = 0, + + // Client to server + Authenticate = 1, + // Server to client + Ready, + + // Client to server + PointerMotion = 10, + PointerAxis, + PointerAxisFinal, + PointerButtonPressed, + PointerButtonReleased, + PointerButtonTapped, + KeyboardButtonPressed, + KeyboardButtonReleased, + KeyboardButtonTapped +} + +struct RemoteDesktopSession<'a> { + proxy: RemoteDesktop<'a>, + session: Session<'a, RemoteDesktop<'a>>, +} + + +async fn create_remote_desktop_session<'a>() -> ashpd::Result>> { + let proxy = RemoteDesktop::new().await?; + let session = proxy.create_session().await?; + proxy + .select_devices( + &session, + DeviceType::Keyboard | DeviceType::Pointer, + None, + PersistMode::DoNot, + ) + .await?; + + let response = proxy + .start(&session, &WindowIdentifier::default()) + .await? + .response()?; + println!("{:#?}", response.devices()); + + Ok(Arc::new(RemoteDesktopSession { proxy, session })) +} + +async fn handle_gateway_connection(ws: WebSocket, session: Arc>) { + let (mut user_ws_tx, mut user_ws_rx) = ws.split(); + let mut authenticated = false; + + while let Some(result) = user_ws_rx.next().await { + let msg = match result { + Ok(msg) => msg, + Err(_e) => { + break; + } + }; + if !msg.is_binary() { + continue; + } + + let mut bytes = msg.as_bytes(); + if bytes.remaining() < 1 { + continue; + } + let frame_type = FromPrimitive::from_u8(bytes.get_u8()); + println!("frame: {:?}", frame_type); + match frame_type { + Some(FrameType::Authenticate) => { + if authenticated { + continue; + } + + // Here we'd check a password or something + authenticated = true; + let mut out_bytes = BytesMut::with_capacity(1); + out_bytes.put_u8(FrameType::Ready as u8); + if let Err(_) = user_ws_tx.send(Message::binary(out_bytes)).await { + return; + } + }, + Some(FrameType::PointerMotion) => { + if !authenticated { + continue; + } + + // PointerMotion payload: + // f32 dx, f32 dy + if bytes.remaining() < 8 { + continue; + } + + let dx = bytes.get_f32(); + let dy = bytes.get_f32(); + + let _ = session.proxy.notify_pointer_motion(&session.session, dx.into(), dy.into()).await; + }, + Some(FrameType::PointerAxis) => { + if !authenticated { + continue; + } + + // PointerAxis payload: + // f32 dx, f32 dy + if bytes.remaining() < 8 { + continue; + } + + let dx = bytes.get_f32(); + let dy = bytes.get_f32(); + + let _ = session.proxy.notify_pointer_axis(&session.session, dx.into(), dy.into(), false).await; + }, + Some(FrameType::PointerAxisFinal) => { + if !authenticated { + continue; + } + + // PointerAxisFinal payload: + // f32 dx, f32 dy + if bytes.remaining() < 8 { + continue; + } + + let dx = bytes.get_f32(); + let dy = bytes.get_f32(); + + let _ = session.proxy.notify_pointer_axis(&session.session, dx.into(), dy.into(), true).await; + }, + Some(FrameType::PointerButtonPressed) => { + if !authenticated { + continue; + } + + // PointerButtonPressed payload: + // i32 button + if bytes.remaining() < 4 { + continue; + } + + let button = bytes.get_i32(); + + let _ = session.proxy.notify_pointer_button(&session.session, button, KeyState::Pressed).await; + }, + Some(FrameType::PointerButtonReleased) => { + if !authenticated { + continue; + } + + // PointerButtonReleased payload: + // i32 button + if bytes.remaining() < 4 { + continue; + } + + let button = bytes.get_i32(); + + let _ = session.proxy.notify_pointer_button(&session.session, button, KeyState::Released).await; + }, + Some(FrameType::PointerButtonTapped) => { + if !authenticated { + continue; + } + + // PointerButtonTapped payload: + // i32 button + if bytes.remaining() < 4 { + continue; + } + + let button = bytes.get_i32(); + + let _ = session.proxy.notify_pointer_button(&session.session, button, KeyState::Pressed).await; + let _ = session.proxy.notify_pointer_button(&session.session, button, KeyState::Released).await; + }, + Some(FrameType::KeyboardButtonPressed) => { + if !authenticated { + continue; + } + + // KeyboardButtonPressed payload: + // i32 code + if bytes.remaining() < 4 { + continue; + } + + let code = bytes.get_i32(); + + let _ = session.proxy.notify_keyboard_keysym(&session.session, code, KeyState::Pressed).await; + }, + Some(FrameType::KeyboardButtonReleased) => { + if !authenticated { + continue; + } + + // KeyboardButtonReleased payload: + // i32 code + if bytes.remaining() < 4 { + continue; + } + + let code = bytes.get_i32(); + + let _ = session.proxy.notify_keyboard_keysym(&session.session, code, KeyState::Released).await; + }, + Some(FrameType::KeyboardButtonTapped) => { + if !authenticated { + continue; + } + + // KeyboardButtonTapped payload: + // i32 code + if bytes.remaining() < 4 { + continue; + } + + let code = bytes.get_i32(); + + let _ = session.proxy.notify_keyboard_keysym(&session.session, code, KeyState::Pressed).await; + let _ = session.proxy.notify_keyboard_keysym(&session.session, code, KeyState::Released).await; + }, + + // Invalid frame types + None | Some(FrameType::None) | Some(FrameType::Ready) => {} + } + } +} + +#[tokio::main] +async fn main() -> ExitCode { + let session = if let Ok(x) = create_remote_desktop_session().await { x } else { return ExitCode::from(1); }; + let session = warp::any().map(move || session.clone()); + + let chat = warp::path("gateway") + .and(warp::ws()) + .and(session) + .map(|ws: warp::ws::Ws, session| { + ws.on_upgrade(move |socket| handle_gateway_connection(socket, session)) + }); + + let assets = warp::path("assets") + .and(warp::fs::dir("frontend/dist/assets")); + + let root = warp::path::end() + .and(warp::fs::file("frontend/dist/index.html")) + .or(assets); + + warp::serve(chat.or(root)) + .run(([0, 0, 0, 0], 3030)) + .await; + + return ExitCode::from(0); +}