diff --git a/Cargo.lock b/Cargo.lock index 9d58167df..acdd46ade 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1273,7 +1273,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.9.0", + "toml 0.9.2", "vswhom", "winreg 0.55.0", ] @@ -1431,6 +1431,19 @@ dependencies = [ "miniz_oxide 0.8.0", ] +[[package]] +name = "flexi_logger" +version = "0.29.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a5a6882b2e137c4f2664562995865084eb5a00611fba30c582ef10354c4ad8" +dependencies = [ + "chrono", + "log", + "nu-ansi-term", + "regex", + "thiserror 2.0.12", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2770,6 +2783,15 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -4380,6 +4402,7 @@ dependencies = [ "const_format", "dirs-next", "discord-sdk", + "flexi_logger", "glob", "itertools", "libloading 0.8.5", @@ -4759,7 +4782,7 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.9.0", + "toml 0.9.2", "walkdir", ] @@ -4817,7 +4840,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.9.0", + "toml 0.9.2", "walkdir", ] @@ -4857,7 +4880,7 @@ dependencies = [ "tauri-plugin", "tauri-utils", "thiserror 2.0.12", - "toml 0.9.0", + "toml 0.9.2", "url", ] @@ -5067,7 +5090,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror 2.0.12", - "toml 0.9.0", + "toml 0.9.2", "url", "urlpattern", "uuid", @@ -5284,9 +5307,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f271e09bde39ab52250160a67e88577e0559ad77e9085de6e9051a2c4353f8f8" +checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" dependencies = [ "indexmap 2.6.0", "serde", @@ -5294,7 +5317,7 @@ dependencies = [ "toml_datetime 0.7.0", "toml_parser", "toml_writer", - "winnow 0.7.11", + "winnow 0.7.12", ] [[package]] @@ -5348,16 +5371,16 @@ dependencies = [ "serde_spanned 0.6.9", "toml_datetime 0.6.11", "toml_write", - "winnow 0.7.11", + "winnow 0.7.12", ] [[package]] name = "toml_parser" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5c1c469eda89749d2230d8156a5969a69ffe0d6d01200581cdc6110674d293e" +checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" dependencies = [ - "winnow 0.7.11", + "winnow 0.7.12", ] [[package]] @@ -5368,9 +5391,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b679217f2848de74cabd3e8fc5e6d66f40b7da40f8e1954d92054d9010690fd5" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" [[package]] name = "tower-service" @@ -6430,9 +6453,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -6656,7 +6679,7 @@ dependencies = [ "tracing", "uds_windows", "windows-sys 0.59.0", - "winnow 0.7.11", + "winnow 0.7.12", "xdg-home", "zbus_macros 5.4.0", "zbus_names 4.2.0", @@ -6711,7 +6734,7 @@ checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" dependencies = [ "serde", "static_assertions", - "winnow 0.7.11", + "winnow 0.7.12", "zvariant 5.7.0", ] @@ -6819,7 +6842,7 @@ dependencies = [ "endi", "enumflags2", "serde", - "winnow 0.7.11", + "winnow 0.7.12", "zvariant_derive 5.7.0", "zvariant_utils 3.2.1", ] @@ -6871,5 +6894,5 @@ dependencies = [ "quote", "serde", "syn 2.0.87", - "winnow 0.7.11", + "winnow 0.7.12", ] diff --git a/TRADEMARK.md b/TRADEMARK.md index a4332e3a3..919ae7188 100644 --- a/TRADEMARK.md +++ b/TRADEMARK.md @@ -1,33 +1,33 @@ -## SlimeVR is a trademark or a registered trademark of SlimeVR B.V. - -**Usage of SlimeVR software, hardware, or other intellectual property in this or other repositories does not grant you the right to use SlimeVR trademark as your own.** - -The purpose of a trademark is to remove uncertainty for users and customers regarding the product's manufacturer or endorsement. You're not allowed to market your product using SlimeVR name, and your usage of the name should be only factual and descriptive. For example, calling original SlimeVR products SlimeVR or describing compatibility of other products or derivatives. This applies to all products, including software, and hardware including non-official Full-Body Trackers. - -**Here are a few _acceptable_ uses of SlimeVR name when selling unofficial Slime trackers:** -* SlimeVR-compatible trackers -* Unofficial SlimeVR trackers / Non-official SlimeVR trackers -* DIY SlimeVR trackers -* Third-party SlimeVR Trackers -* Custom SlimeVR-compatible trackers -* < Your Brand > Slime Trackers -* Using "SlimeVR" as a search tag - -**_Unacceptable_ uses include, but are not limited to:** -* SlimeVR store -* Buy SlimeVR -* SlimeVR Trackers -* Original SlimeVR -* Official SlimeVR -* SlimeVR BMI270 (or any other IMU model along with SlimeVR name) -* < Your brand > SlimeVR / < your brand > SlimeVR Trackers - -Use of the SlimeVR name that can cause confusion is not allowed in any part of the listing, including, but not limited to: product title, product description, product metadata, site title, site name, site metadata, site texts, social media posts, or other advertisement. - -Also, please ensure you use the correct spelling and capitalization: only **"SlimeVR" is acceptable**. Not "Slimevr", "slimevr", or "Slime VR". You're allowed to use the word "slime" as you wish, it's not a trademark. - -Please understand that we have an obligation to reduce confusion for the customers, and we believe that our usage terms are generous compared to many other companies and products. This applies to all sellers or derivative products, we do not make exceptions. - ---- - +## SlimeVR is a trademark or a registered trademark of SlimeVR B.V. + +**Usage of SlimeVR software, hardware, or other intellectual property in this or other repositories does not grant you the right to use SlimeVR trademark as your own.** + +The purpose of a trademark is to remove uncertainty for users and customers regarding the product's manufacturer or endorsement. You're not allowed to market your product using SlimeVR name, and your usage of the name should be only factual and descriptive. For example, calling original SlimeVR products SlimeVR or describing compatibility of other products or derivatives. This applies to all products, including software, and hardware including non-official Full-Body Trackers. + +**Here are a few _acceptable_ uses of SlimeVR name when selling unofficial Slime trackers:** +* SlimeVR-compatible trackers +* Unofficial SlimeVR trackers / Non-official SlimeVR trackers +* DIY SlimeVR trackers +* Third-party SlimeVR Trackers +* Custom SlimeVR-compatible trackers +* < Your Brand > Slime Trackers +* Using "SlimeVR" as a search tag + +**_Unacceptable_ uses include, but are not limited to:** +* SlimeVR store +* Buy SlimeVR +* SlimeVR Trackers +* Original SlimeVR +* Official SlimeVR +* SlimeVR BMI270 (or any other IMU model along with SlimeVR name) +* < Your brand > SlimeVR / < your brand > SlimeVR Trackers + +Use of the SlimeVR name that can cause confusion is not allowed in any part of the listing, including, but not limited to: product title, product description, product metadata, site title, site name, site metadata, site texts, social media posts, or other advertisement. + +Also, please ensure you use the correct spelling and capitalization: only **"SlimeVR" is acceptable**. Not "Slimevr", "slimevr", or "Slime VR". You're allowed to use the word "slime" as you wish, it's not a trademark. + +Please understand that we have an obligation to reduce confusion for the customers, and we believe that our usage terms are generous compared to many other companies and products. This applies to all sellers or derivative products, we do not make exceptions. + +--- + If you have any questions about SlimeVR trademark or copyrighted materials, you can reach out to us at *tm[at]slimevr.dev*. \ No newline at end of file diff --git a/gui/package.json b/gui/package.json index 7a2f528ea..761c0a15f 100644 --- a/gui/package.json +++ b/gui/package.json @@ -16,15 +16,15 @@ "@sentry/vite-plugin": "^2.22.7", "@tailwindcss/typography": "^0.5.15", "@tanstack/react-query": "^5.48.0", - "@tauri-apps/api": "~2", - "@tauri-apps/plugin-dialog": "~2", - "@tauri-apps/plugin-fs": "~2", - "@tauri-apps/plugin-http": "~2", + "@tauri-apps/api": "^2.0.2", + "@tauri-apps/plugin-dialog": "^2.0.0", + "@tauri-apps/plugin-fs": "2.4.1", + "@tauri-apps/plugin-opener": "^2.4.0", + "@tauri-apps/plugin-http": "^2.5.0", "@tauri-apps/plugin-log": "~2", - "@tauri-apps/plugin-opener": "~2", - "@tauri-apps/plugin-os": "~2", - "@tauri-apps/plugin-shell": "~2", - "@tauri-apps/plugin-store": "~2", + "@tauri-apps/plugin-os": "^2.0.0", + "@tauri-apps/plugin-shell": "^2.3.0", + "@tauri-apps/plugin-store": "^2.4.1", "@tweenjs/tween.js": "^25.0.0", "@twemoji/svg": "^15.0.0", "ajv": "^8.17.1", diff --git a/gui/public/i18n/en/translation.ftl b/gui/public/i18n/en/translation.ftl index c26ef6e2f..8123e8802 100644 --- a/gui/public/i18n/en/translation.ftl +++ b/gui/public/i18n/en/translation.ftl @@ -238,9 +238,9 @@ reset-reset_all_warning_default-v2 = Are you sure you want to do this? reset-full = Full Reset -reset-mounting = Reset Mounting -reset-mounting-feet = Reset Feet Mounting -reset-mounting-fingers = Reset Fingers Mounting +reset-mounting = Mounting Calibration +reset-mounting-feet = Feet Calibration +reset-mounting-fingers = Fingers Calibration reset-yaw = Yaw Reset ## Serial detection stuff @@ -262,6 +262,7 @@ navbar-settings = Settings ## Biovision hierarchy recording bvh-start_recording = Record BVH +bvh-stop_recording = Save BVH recording bvh-recording = Recording... bvh-save_title = Save BVH recording @@ -277,8 +278,8 @@ widget-overlay-is_mirrored_label = Display Overlay as Mirror ## Widget: Drift compensation widget-drift_compensation-clear = Clear drift compensation -## Widget: Clear Reset Mounting -widget-clear_mounting = Clear Reset Mounting +## Widget: Clear Mounting calibration +widget-clear_mounting = Clear mounting calibration ## Widget: Developer settings widget-developer_mode = Developer Mode @@ -455,6 +456,7 @@ mounting_selection_menu-close = Close ## Sidebar settings settings-sidebar-title = Settings settings-sidebar-general = General +settings-sidebar-steamvr = SteamVR settings-sidebar-tracker_mechanics = Tracker mechanics settings-sidebar-stay_aligned = Stay Aligned settings-sidebar-fk_settings = Tracking settings @@ -462,9 +464,12 @@ settings-sidebar-gesture_control = Gesture control settings-sidebar-interface = Interface settings-sidebar-osc_router = OSC router settings-sidebar-osc_trackers = VRChat OSC Trackers +settings-sidebar-osc_vmc = VMC settings-sidebar-utils = Utilities settings-sidebar-serial = Serial console settings-sidebar-appearance = Appearance +settings-sidebar-home = Home Screen +settings-sidebar-checklist = Tracking checklist settings-sidebar-notifications = Notifications settings-sidebar-behavior = Behavior settings-sidebar-firmware-tool = DIY Firmware Tool @@ -654,7 +659,7 @@ settings-general-gesture_control-yawResetTaps = Taps for yaw reset settings-general-gesture_control-fullResetEnabled = Enable tap to full reset settings-general-gesture_control-fullResetDelay = Full reset delay settings-general-gesture_control-fullResetTaps = Taps for full reset -settings-general-gesture_control-mountingResetEnabled = Enable tap to reset mounting +settings-general-gesture_control-mountingResetEnabled = Enable tap to mounting calibration settings-general-gesture_control-mountingResetDelay = Mounting reset delay settings-general-gesture_control-mountingResetTaps = Taps for mounting reset # The number of trackers that can have higher acceleration before a tap is rejected @@ -879,6 +884,16 @@ settings-utils-advanced-open_logs = Logs folder settings-utils-advanced-open_logs-description = Open SlimeVR's logs folder in file explorer, containing the logs of the app settings-utils-advanced-open_logs-label = Open folder +## Home Screen +settings-home-list-layout = Trackers list layout +settings-home-list-layout-desc = Select one of the possible layouts of the home screen +settings-home-list-layout-grid = Grid +settings-home-list-layout-table = Table + +## Tracking Checlist +settings-tracking_checklist-active_steps = Active Steps +settings-tracking_checklist-active_steps-desc = List all the steps that will show in the tracking checklist. You can either disable or enable ignorable steps + ## Setup/onboarding menu onboarding-skip = Skip setup onboarding-continue = Continue @@ -1132,7 +1147,11 @@ onboarding-automatic_mounting-done-description = Your mounting calibration is co onboarding-automatic_mounting-done-restart = Try again onboarding-automatic_mounting-mounting_reset-title = Mounting Reset onboarding-automatic_mounting-mounting_reset-step-0 = 1. Squat in a "skiing" pose with your legs bent, your upper body tilted forwards, and your arms bent. -onboarding-automatic_mounting-mounting_reset-step-1 = 2. Press the "Reset Mounting" button and wait for 3 seconds before the trackers' mounting orientations will reset. +onboarding-automatic_mounting-mounting_reset-step-1 = 2. Press the "Mounting calibration" button and wait for 3 seconds before the trackers' mounting orientations will reset. + +onboarding-automatic_mounting-mounting_reset-feet-step-0 = 1. Stand on your toes with both feet pointing forward. Alternatively you can do it siting on a chair. +onboarding-automatic_mounting-mounting_reset-feet-step-1 = 2. Press the "Feet calibration" button and wait for 3 seconds before the trackers' mounting orientations will reset. + onboarding-automatic_mounting-preparation-title = Preparation onboarding-automatic_mounting-preparation-v2-step-0 = 1. Press the "Full Reset" button. onboarding-automatic_mounting-preparation-v2-step-1 = 2. Stand upright with your arms to your sides. Make sure to look forward. @@ -1305,6 +1324,8 @@ onboarding-stay_aligned-done = Done ## Home home-no_trackers = No trackers detected or assigned +home-settings = Home Page Settings +home-settings-close = Close ## Trackers Still On notification trackers_still_on-modal-title = Trackers still on @@ -1522,3 +1543,58 @@ error_collection_modal-description_v2 = { settings-interface-behavior-error_trac You can change this setting later in the Behavior section of the settings page. error_collection_modal-confirm = I agree error_collection_modal-cancel = I don't want to + + +tracking_checklist = Tracking Checklist +tracking_checklist-settings = Tracking Checklist Settings +tracking_checklist-settings-close = Close +tracking_checklist-status-incomplete = You are not prepared to use SlimeVR! +tracking_checklist-status-partial = {$count -> + [one] You have 1 warning! + *[many] You have {$count} warnings! +} +tracking_checklist-status-complete = You are prepared to use SlimeVR! +tracking_checklist-MOUNTING_CALIBRATION = Perform a mounting calibration +tracking_checklist-FEET_MOUNTING_CALIBRATION = Perform a feet mounting calibration +tracking_checklist-FULL_RESET = Perform a full Reset +tracking_checklist-FULL_RESET-desc = Some Trackers need a reset to be performed +tracking_checklist-STEAMVR_DISCONNECTED = SteamVR not running +tracking_checklist-STEAMVR_DISCONNECTED-desc = SteamVR is not running. Are you using it for vr? +tracking_checklist-STEAMVR_DISCONNECTED-open = Launch SteamVR +tracking_checklist-TRACKERS_REST_CALIBRATION = Calibrate your trackers +tracking_checklist-TRACKERS_REST_CALIBRATION-desc = You didn't perform tracker calibration. Please let your Slimes (highlighted in yellow) rest on a stable surface for a few secconds. +tracking_checklist-TRACKER_ERROR = Trackers with Errors +tracking_checklist-TRACKER_ERROR-desc = Some of your trackers have an error. Please restart the tracker. +tracking_checklist-VRCHAT_SETTINGS = Configure VRChat settings +tracking_checklist-VRCHAT_SETTINGS-desc = You have misconfigured VRchat Settings! This can impact your tracking experience. +tracking_checklist-VRCHAT_SETTINGS-open = Go to VRChat Warnings +tracking_checklist-UNASSIGNED_HMD = VR Headset not assigned to Head +tracking_checklist-UNASSIGNED_HMD-desc = The VR headset should be assigned as a head tracker. +tracking_checklist-NETWORK_PROFILE_PUBLIC = Change your network profile +tracking_checklist-NETWORK_PROFILE_PUBLIC-desc = {$count -> + [one] Your network profile is currently set to Public ({$adapters}). + This is not recommended for SlimeVR to function properly. + See how to fix it here. + *[many] Some of your network adapters are set to public: + {$adapters} + This is not recommended for SlimeVR to function properly. + See how to fix it here. +} +tracking_checklist-NETWORK_PROFILE_PUBLIC-open = Open Control Panel +tracking_checklist-STAY_ALIGNED_CONFIGURED = Configure Stay Aligned +tracking_checklist-STAY_ALIGNED_CONFIGURED-desc = Record the Stay Aligned poses for an improved imu drift +tracking_checklist-STAY_ALIGNED_CONFIGURED-open = Open Stay Aligned Wizard + +tracking_checklist-ignore = Ignore + +preview-mocap_mode_soon = Mocap Mode (Soon™) +preview-disable_render = Disable rendering +preview-disabled_render = Rendering disabled + +toolbar-mounting_calibration = Mounting Calibration +toolbar-mounting_calibration-default = Body +toolbar-mounting_calibration-feet = Feet +toolbar-mounting_calibration-fingers = Fingers +toolbar-drift_reset = Drift Reset +toolbar-assigned_trackers = {$count} trackers assigned +toolbar-unassigned_trackers = {$count} trackers unassigned diff --git a/gui/public/images/mounting/MountingFeets.webp b/gui/public/images/mounting/MountingFeets.webp new file mode 100644 index 000000000..e56c6fc13 Binary files /dev/null and b/gui/public/images/mounting/MountingFeets.webp differ diff --git a/gui/public/images/mounting/MountingFeetsSide.webp b/gui/public/images/mounting/MountingFeetsSide.webp new file mode 100644 index 000000000..3b1240e06 Binary files /dev/null and b/gui/public/images/mounting/MountingFeetsSide.webp differ diff --git a/gui/public/sounds/full-reset/end-full-reset-with-tail.ogg b/gui/public/sounds/full-reset/end-full-reset-with-tail.ogg new file mode 100644 index 000000000..c390da3dd Binary files /dev/null and b/gui/public/sounds/full-reset/end-full-reset-with-tail.ogg differ diff --git a/gui/public/sounds/full-reset/full-click-1.ogg b/gui/public/sounds/full-reset/full-click-1.ogg new file mode 100644 index 000000000..4d392fa6e Binary files /dev/null and b/gui/public/sounds/full-reset/full-click-1.ogg differ diff --git a/gui/public/sounds/full-reset/full-click-2.ogg b/gui/public/sounds/full-reset/full-click-2.ogg new file mode 100644 index 000000000..b7f7794e7 Binary files /dev/null and b/gui/public/sounds/full-reset/full-click-2.ogg differ diff --git a/gui/public/sounds/full-reset/full-click-3.ogg b/gui/public/sounds/full-reset/full-click-3.ogg new file mode 100644 index 000000000..1a53f97a4 Binary files /dev/null and b/gui/public/sounds/full-reset/full-click-3.ogg differ diff --git a/gui/public/sounds/full-reset/init-full-reset-with-tail.ogg b/gui/public/sounds/full-reset/init-full-reset-with-tail.ogg new file mode 100644 index 000000000..a1df7a325 Binary files /dev/null and b/gui/public/sounds/full-reset/init-full-reset-with-tail.ogg differ diff --git a/gui/public/sounds/mew.ogg b/gui/public/sounds/mew.ogg new file mode 100644 index 000000000..5b7de503e Binary files /dev/null and b/gui/public/sounds/mew.ogg differ diff --git a/gui/public/sounds/mounting-reset/end-mounting-reset-with-tail.ogg b/gui/public/sounds/mounting-reset/end-mounting-reset-with-tail.ogg new file mode 100644 index 000000000..72aee9838 Binary files /dev/null and b/gui/public/sounds/mounting-reset/end-mounting-reset-with-tail.ogg differ diff --git a/gui/public/sounds/mounting-reset/init-mounting-reset-with-tail.ogg b/gui/public/sounds/mounting-reset/init-mounting-reset-with-tail.ogg new file mode 100644 index 000000000..c3104a524 Binary files /dev/null and b/gui/public/sounds/mounting-reset/init-mounting-reset-with-tail.ogg differ diff --git a/gui/public/sounds/mounting-reset/mount-click-1.ogg b/gui/public/sounds/mounting-reset/mount-click-1.ogg new file mode 100644 index 000000000..4df5da9b5 Binary files /dev/null and b/gui/public/sounds/mounting-reset/mount-click-1.ogg differ diff --git a/gui/public/sounds/mounting-reset/mount-click-2.ogg b/gui/public/sounds/mounting-reset/mount-click-2.ogg new file mode 100644 index 000000000..1a0950677 Binary files /dev/null and b/gui/public/sounds/mounting-reset/mount-click-2.ogg differ diff --git a/gui/public/sounds/mounting-reset/mount-click-3.ogg b/gui/public/sounds/mounting-reset/mount-click-3.ogg new file mode 100644 index 000000000..f3c7a14a3 Binary files /dev/null and b/gui/public/sounds/mounting-reset/mount-click-3.ogg differ diff --git a/gui/public/sounds/tracking/pause.ogg b/gui/public/sounds/tracking/pause.ogg new file mode 100644 index 000000000..5bc5c3601 Binary files /dev/null and b/gui/public/sounds/tracking/pause.ogg differ diff --git a/gui/public/sounds/tracking/play.ogg b/gui/public/sounds/tracking/play.ogg new file mode 100644 index 000000000..9abee87ca Binary files /dev/null and b/gui/public/sounds/tracking/play.ogg differ diff --git a/gui/public/sounds/yaw-reset/yaw-reset.ogg b/gui/public/sounds/yaw-reset/yaw-reset.ogg new file mode 100644 index 000000000..9a7578b64 Binary files /dev/null and b/gui/public/sounds/yaw-reset/yaw-reset.ogg differ diff --git a/gui/src-tauri/Cargo.toml b/gui/src-tauri/Cargo.toml index b43c5c442..b5b855eac 100644 --- a/gui/src-tauri/Cargo.toml +++ b/gui/src-tauri/Cargo.toml @@ -28,15 +28,14 @@ shadow-rs = "0.35" [dependencies] serde_json = "1" serde = { version = "1", features = ["derive"] } -tauri = { version = "2", features = ["devtools", "tray-icon", "image-png", "rustls-tls"] } -tauri-runtime = "2" -tauri-plugin-dialog = "2" -tauri-plugin-fs = "2" -tauri-plugin-http = "2" -tauri-plugin-opener = "2" -tauri-plugin-os = "2" -tauri-plugin-shell = "2" -tauri-plugin-store = "2" +tauri = { version = "2.0", features = ["devtools", "tray-icon", "image-png", "rustls-tls"] } +tauri-runtime = "2.0" +tauri-plugin-dialog = "2.0" +tauri-plugin-fs = "2.4.1" +tauri-plugin-os = "2.0" +tauri-plugin-shell = "2.3.0" +tauri-plugin-store = "2.0" +flexi_logger = "0.29" log-panics = { version = "2", features = ["with-backtrace"] } log = "0.4" clap = { version = "4.0.29", features = ["derive"] } @@ -55,6 +54,8 @@ dirs-next = "2.0.0" discord-sdk = "0.3.6" tokio = { version = "1.37.0", features = ["time"] } itertools = "0.13.0" +tauri-plugin-opener = "2.4.0" +tauri-plugin-http = "2.5.0" tauri-plugin-log = "2" [target.'cfg(windows)'.dependencies] diff --git a/gui/src-tauri/capabilities/migrated.json b/gui/src-tauri/capabilities/migrated.json index e0e3ea5b9..3d4eabb87 100644 --- a/gui/src-tauri/capabilities/migrated.json +++ b/gui/src-tauri/capabilities/migrated.json @@ -41,6 +41,17 @@ } ] }, + { + "identifier": "opener:allow-open-url", + "allow": [ + { + "url": "steam:*" + }, + { + "url": "ms-settings:network" + } + ] + }, { "identifier": "http:default", "allow": [ @@ -52,4 +63,4 @@ "opener:default", "log:default" ] -} \ No newline at end of file +} diff --git a/gui/src/App.tsx b/gui/src/App.tsx index d9335f538..53fa35205 100644 --- a/gui/src/App.tsx +++ b/gui/src/App.tsx @@ -39,7 +39,6 @@ import { OSCRouterSettings } from './components/settings/pages/OSCRouterSettings import * as os from '@tauri-apps/plugin-os'; import { VMCSettings } from './components/settings/pages/VMCSettings'; import { MountingChoose } from './components/onboarding/pages/mounting/MountingChoose'; -import { StatusProvider } from './components/providers/StatusSystemContext'; import { VersionUpdateModal } from './components/VersionUpdateModal'; import { CalibrationTutorialPage } from './components/onboarding/pages/CalibrationTutorial'; import { AssignmentTutorialPage } from './components/onboarding/pages/assignment-preparation/AssignmentTutorial'; @@ -61,6 +60,9 @@ import { FirmwareUpdate } from './components/firmware-update/FirmwareUpdate'; import { ConnectionLost } from './components/onboarding/pages/ConnectionLost'; import { VRCWarningsPage } from './components/vrc/VRCWarningsPage'; import { StayAlignedSetup } from './components/onboarding/pages/stay-aligned/StayAlignedSetup'; +import { TrackingChecklistProvider } from './components/tracking-checklist/TrackingChecklistProvider'; +import { HomeScreenSettings } from './components/settings/pages/HomeScreenSettings'; +import { ChecklistPage } from './components/tracking-checklist/TrackingChecklist'; export const GH_REPO = 'SlimeVR/SlimeVR-Server'; export const VersionContext = createContext(''); @@ -83,7 +85,7 @@ function Layout() { + } @@ -91,7 +93,7 @@ function Layout() { + } @@ -99,11 +101,19 @@ function Layout() { + } /> + + + + } + /> + } @@ -135,6 +145,7 @@ function Layout() { } /> } /> } /> + } /> } /> - +
@@ -305,7 +316,7 @@ export default function App() { {websocketAPI.isConnected && }
-
+
diff --git a/gui/src/components/ClearMountingButton.tsx b/gui/src/components/ClearMountingButton.tsx deleted file mode 100644 index 384ab4aa2..000000000 --- a/gui/src/components/ClearMountingButton.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { Localized } from '@fluent/react'; -import { ClearMountingResetRequestT, RpcMessage } from 'solarxr-protocol'; -import { useWebsocketAPI } from '@/hooks/websocket-api'; -import { BigButton } from './commons/BigButton'; -import { TrashIcon } from './commons/icon/TrashIcon'; -import { Quaternion } from 'three'; -import { QuaternionFromQuatT, similarQuaternions } from '@/maths/quaternion'; -import { useMemo } from 'react'; -import { useAtomValue } from 'jotai'; -import { assignedTrackersAtom } from '@/store/app-store'; - -const _q = new Quaternion(); - -export function ClearMountingButton() { - const { sendRPCPacket } = useWebsocketAPI(); - const assignedTrackers = useAtomValue(assignedTrackersAtom); - - const trackerWithMounting = useMemo( - () => - assignedTrackers.some( - (d) => - !similarQuaternions( - QuaternionFromQuatT(d?.tracker.info?.mountingResetOrientation), - _q - ) - ), - [assignedTrackers] - ); - - const clearMounting = () => { - const record = new ClearMountingResetRequestT(); - sendRPCPacket(RpcMessage.ClearMountingResetRequest, record); - }; - - return ( - - } - onClick={clearMounting} - disabled={!trackerWithMounting} - /> - - ); -} diff --git a/gui/src/components/MainLayout.scss b/gui/src/components/MainLayout.scss index 53c9dc396..a304b3a25 100644 --- a/gui/src/components/MainLayout.scss +++ b/gui/src/components/MainLayout.scss @@ -1,24 +1,67 @@ +:root { + --toolbar-h: 120px; +} + .main-layout { display: grid; grid-template: 't t' var(--topbar-h) - 's c' calc(100% - var(--topbar-h)) + 'n c' calc(100% - var(--topbar-h)) / var(--navbar-w) calc(100% - var(--navbar-w)); - &:has(.widgets) { + &.full { grid-template: 't t t' var(--topbar-h) - 's c w' calc(100% - var(--topbar-h)) - / var(--navbar-w) calc(100% - var(--navbar-w) - var(--widget-w)) var( - --widget-w - ); + 'n b s' var(--toolbar-h) + 'n c s' calc(100% - var(--topbar-h) - var(--toolbar-h)) + / var(--navbar-w) calc(100% - var(--navbar-w) - var(--right-section-w)) var(--right-section-w); + } + + @screen nsm { + --right-section-w: 40%; + } + + @screen sm { + --right-section-w: 35%; + } + + @screen md { + --right-section-w: 30%; + } + + @screen lg { + --right-section-w: 25%; + } + + @screen xl { + --right-section-w: 22%; } @screen mobile { + --checklist-h: 30px; + + &.checklist-ok { + --checklist-h: 0px; + } + + &.full { + grid-template: + 't' var(--topbar-h) + 'l' var(--checklist-h) + 'b' var(--toolbar-h) + 'c' calc( + 100% - var(--topbar-h) - var(--checklist-h) - var(--toolbar-h) - var( + --navbar-h + ) + ) + 'n' calc(var(--navbar-h)) + / 100%; + } + grid-template: 't' var(--topbar-h) 'c' calc(100% - var(--topbar-h) - var(--navbar-h)) - 's' calc(var(--navbar-h)) + 'n' calc(var(--navbar-h)) / 100%; } } diff --git a/gui/src/components/MainLayout.tsx b/gui/src/components/MainLayout.tsx index a8c6b81b0..c3ca4dde0 100644 --- a/gui/src/components/MainLayout.tsx +++ b/gui/src/components/MainLayout.tsx @@ -9,20 +9,25 @@ import { import { Navbar } from './Navbar'; import { TopBar } from './TopBar'; import { useWebsocketAPI } from '@/hooks/websocket-api'; -import { WidgetsComponent } from './WidgetsComponent'; import './MainLayout.scss'; +import { Toolbar } from './Toolbar'; +import { Sidebar } from './Sidebar'; +import { TrackingChecklistMobile } from './tracking-checklist/TrackingChecklist'; +import { useTrackingChecklist } from '@/hooks/tracking-checklist'; export function MainLayout({ children, background = true, - widgets = true, + full = false, isMobile = undefined, }: { children: ReactNode; background?: boolean; isMobile?: boolean; - widgets?: boolean; + showToolbarSettings?: boolean; + full?: boolean; }) { + const { completion } = useTrackingChecklist(); const { sendRPCPacket } = useWebsocketAPI(); const [ProportionsLastPageOpen, setProportionsLastPageOpen] = useState(true); @@ -58,33 +63,42 @@ export function MainLayout({ }); return ( -
-
-
- -
-
- -
-
- {children} -
- {!isMobile && widgets && ( -
- -
- )} +
+
+
+
+ +
+ +
+ {children} +
+ {full && isMobile && completion !== 'complete' && ( + + )} + {full && ( +
+ +
+ )} + {!isMobile && full && ( +
+ +
+ )}
); } diff --git a/gui/src/components/Navbar.tsx b/gui/src/components/Navbar.tsx index cfaf81d00..a2d3f8cc6 100644 --- a/gui/src/components/Navbar.tsx +++ b/gui/src/components/Navbar.tsx @@ -2,14 +2,14 @@ import { useLocalization } from '@fluent/react'; import classnames from 'classnames'; import { ReactNode } from 'react'; import { NavLink, useMatch } from 'react-router-dom'; -import { CubeIcon } from './commons/icon/CubeIcon'; import { GearIcon } from './commons/icon/GearIcon'; import { HumanIcon } from './commons/icon/HumanIcon'; import { RulerIcon } from './commons/icon/RulerIcon'; import { SparkleIcon } from './commons/icon/SparkleIcon'; -import { WrenchIcon } from './commons/icon/WrenchIcons'; import { useBreakpoint } from '@/hooks/breakpoint'; import { useConfig } from '@/hooks/config'; +import { HomeIcon } from './commons/icon/HomeIcon'; +import { SkiIcon } from './commons/icon/SkiIcon'; export function NavButton({ to, @@ -34,7 +34,7 @@ export function NavButton({ state={state} className={classnames( 'flex flex-col justify-center xs:gap-4 mobile:gap-2', - 'xs:w-[85px] mobile:w-[80px] mobile:h-[80px]', + 'xs:w-[85px] mobile:w-[65px] mobile:h-[65px]', 'xs:py-3 mobile:py-4 rounded-md mobile:rounded-b-none group select-text', { 'bg-accent-background-50 fill-accent-background-20': doesMatch, @@ -44,16 +44,16 @@ export function NavButton({ >
{icon}
- }> + }> {l10n.getString('navbar-home')} } + icon={} > {l10n.getString('navbar-mounting')} diff --git a/gui/src/components/Sidebar.tsx b/gui/src/components/Sidebar.tsx new file mode 100644 index 000000000..97b45e08e --- /dev/null +++ b/gui/src/components/Sidebar.tsx @@ -0,0 +1,251 @@ +import { useTrackingChecklist } from '@/hooks/tracking-checklist'; +import { TrackingChecklist } from './tracking-checklist/TrackingChecklist'; +import { SkeletonVisualizerWidget } from './widgets/SkeletonVisualizerWidget'; +import { useEffect, useLayoutEffect, useMemo, useState } from 'react'; +import classNames from 'classnames'; +import { Typography } from './commons/Typography'; +import { useLocaleConfig } from '@/i18n/config'; +import { useWebsocketAPI } from '@/hooks/websocket-api'; +import { + RpcMessage, + SkeletonConfigRequestT, + SkeletonConfigResponseT, +} from 'solarxr-protocol'; +import { Tooltip } from './commons/Tooltip'; +import { Vector3 } from 'three'; +import { RecordIcon } from './commons/icon/RecordIcon'; +import { PauseIcon } from './commons/icon/PauseIcon'; +import { HumanIcon } from './commons/icon/HumanIcon'; +import { EyeIcon } from './commons/icon/EyeIcon'; +import { useConfig } from '@/hooks/config'; +import { useBHV } from '@/hooks/bvh'; +import { usePauseTracking } from '@/hooks/pause-tracking'; +import { PlayIcon } from './commons/icon/PlayIcon'; + +export function PreviewControls({ open }: { open: boolean }) { + const [userHeight, setUserHeight] = useState(''); + const { currentLocales } = useLocaleConfig(); + const { useRPCPacket, sendRPCPacket } = useWebsocketAPI(); + + const { + state: bvhState, + toggle: toggleBVH, + available: bvhAvailable, + } = useBHV(); + const { paused, toggle: toggleTracking } = usePauseTracking(); + + const { cmFormat } = useMemo(() => { + const cmFormat = Intl.NumberFormat(currentLocales, { + style: 'unit', + unit: 'centimeter', + maximumFractionDigits: 1, + }); + return { cmFormat }; + }, [currentLocales]); + useRPCPacket( + RpcMessage.SkeletonConfigResponse, + (data: SkeletonConfigResponseT) => { + if (data.userHeight) + setUserHeight(cmFormat.format((data.userHeight * 100) / 0.936)); + } + ); + + useEffect(() => { + sendRPCPacket( + RpcMessage.SkeletonConfigRequest, + new SkeletonConfigRequestT() + ); + }, []); + + return ( + <> + + } + > +
+ {userHeight} +
+
+
+
+ {bvhAvailable && ( + + } + preferedDirection="top" + > +
toggleBVH()} + > + {bvhState === 'idle' && } + {bvhState !== 'idle' && ( +
+ )} +
+ + )} + + } + preferedDirection="top" + > +
toggleTracking()} + > + {!paused && } + {paused && } +
+
+ + } + preferedDirection="top" + > +
+ +
+
+
+
+ + ); +} + +function PreviewSection({ open }: { open: boolean }) { + const { config, setConfig } = useConfig(); + const [disabledRender, setDisabledRender] = useState(config?.skeletonPreview); + + const toggleRender = () => { + setConfig({ skeletonPreview: disabledRender }); + }; + + useLayoutEffect(() => { + // need useLayoutEffect to make sure that the state is corect before the first render of the skeleton + setDisabledRender(!config?.skeletonPreview); + }, [config]); + + return ( +
+ toggleRender()} + onInit={(context) => { + context.addView({ + left: 0, + bottom: 0, + width: 1, + height: 1, + position: new Vector3(3, 2.5, -3), + onHeightChange(v, newHeight) { + v.controls.target.set(0, newHeight / 2.2, 0.1); + const scale = Math.max(1, newHeight) / 1.3; + v.camera.zoom = 1 / scale; + }, + }); + }} + /> + } + > +
toggleRender()} + > + +
+
+ +
+ ); +} + +export function Sidebar() { + const { completion } = useTrackingChecklist(); + const [closed, setClosed] = useState(true); + const [closing, setClosing] = useState(false); + + const closedHight = '90px'; + const checklistSize = closed ? closedHight : 'calc(100% - 16px)'; + const previewSize = closed ? `calc(100% - ${closedHight} - 24px)` : '0%'; + + const toggleClosed = () => setClosed((closed) => !closed); + + useLayoutEffect(() => { + setClosing(true); + const ref = setTimeout(() => setClosing(false), 1000); + return () => { + clearTimeout(ref); + setClosing(false); + }; + }, [closed]); + + useEffect(() => { + if (completion === 'complete') { + setClosed(true); + } else if (completion === 'incomplete') { + setClosed(false); + } + }, [completion]); + + return ( + <> +
+ +
+
+ +
+ + ); +} diff --git a/gui/src/components/Toolbar.tsx b/gui/src/components/Toolbar.tsx new file mode 100644 index 000000000..a3558c242 --- /dev/null +++ b/gui/src/components/Toolbar.tsx @@ -0,0 +1,202 @@ +import { Typography } from './commons/Typography'; +import classNames from 'classnames'; +import { ResetType } from 'solarxr-protocol'; +import { + BODY_PARTS_GROUPS, + MountingResetGroup, + ResetBtnStatus, + useReset, + UseResetOptions, +} from '@/hooks/reset'; +import { Tooltip } from './commons/Tooltip'; +import { useAtomValue } from 'jotai'; +import { assignedTrackersAtom } from '@/store/app-store'; +import { useBreakpoint } from '@/hooks/breakpoint'; +import { useMemo } from 'react'; +import { ResetButtonIcon } from './home/ResetButton'; + +const MAINBUTTON_CLASSES = ({ disabled }: { disabled: boolean }) => + classNames( + 'relative overflow-clip', + 'flex h-full items-center justify-center gap-2 px-4 bg-background-60 rounded-lg fill-background-10 aspect-square md:aspect-auto', + { + 'cursor-pointer hover:bg-background-50 bg-background-60': !disabled, + 'cursor-not-allowed bg-background-70 brightness-75': disabled, + } + ); + +function ButtonProgress({ + progress, + status, +}: { + progress: number; + status: ResetBtnStatus; +}) { + return ( +
+ ); +} + +function BasicResetButton(options: UseResetOptions & { customName?: string }) { + const { isMd } = useBreakpoint('md'); + const { + triggerReset, + status, + name: resetName, + timer, + progress: resetProress, + disabled, + duration, + } = useReset(options); + + const progress = status === 'counting' ? resetProress / duration : 0; + + const name = options.customName || resetName; + + const skiReset = + options.type === ResetType.Mounting && options.group === 'default'; + + return ( + } + preferedDirection="top" + > +
!disabled && triggerReset()} + > +
+ +
+ +
+ +
+ + +
+ + {timer} + +
+
+
+ ); +} + +export function Toolbar() { + const assignedTrackers = useAtomValue(assignedTrackersAtom); + + const { visibleGroups, groupVisibility } = useMemo(() => { + const groupVisibility = Object.keys(BODY_PARTS_GROUPS) + .filter((k) => ['fingers'].includes(k)) + .reduce( + (curr, key) => { + const group = key as MountingResetGroup; + curr[group] = assignedTrackers.some( + ({ tracker }) => + tracker.info?.bodyPart && + BODY_PARTS_GROUPS[group].includes(tracker.info?.bodyPart) + ); + + return curr; + }, + {} as Record + ); + + return { + groupVisibility, + visibleGroups: Object.values(groupVisibility).filter((v) => v).length, + }; + }, [assignedTrackers]); + + return ( + <> +
+
+
+ +
+ + +
+
+
+ +
+ + + {groupVisibility['fingers'] && ( + + )} +
+
+
+
+ + ); +} diff --git a/gui/src/components/WidgetsComponent.tsx b/gui/src/components/WidgetsComponent.tsx deleted file mode 100644 index 214e3d21b..000000000 --- a/gui/src/components/WidgetsComponent.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { Localized, useLocalization } from '@fluent/react'; -import { BVHButton } from './BVHButton'; -import { TrackingPauseButton } from './TrackingPauseButton'; -import { ResetButton } from './home/ResetButton'; -import { OverlayWidget } from './widgets/OverlayWidget'; -import { TipBox } from './commons/TipBox'; -import { DeveloperModeWidget } from './widgets/DeveloperModeWidget'; -import { useConfig } from '@/hooks/config'; -import { ResetType, StatusData } from 'solarxr-protocol'; -import { useMemo } from 'react'; -import { parseStatusToLocale, useStatusContext } from '@/hooks/status-system'; -import { ClearMountingButton } from './ClearMountingButton'; -import { ToggleableSkeletonVisualizerWidget } from './widgets/SkeletonVisualizerWidget'; -import { useAtomValue } from 'jotai'; -import { flatTrackersAtom } from '@/store/app-store'; -import { A } from './commons/A'; - -function UnprioritizedStatuses() { - const { l10n } = useLocalization(); - const trackers = useAtomValue(flatTrackersAtom); - const { statuses } = useStatusContext(); - const unprioritizedStatuses = useMemo( - () => Object.values(statuses).filter((status) => !status.prioritized), - [statuses] - ); - - return ( -
- {unprioritizedStatuses.map((status) => ( - - ), - }} - > - - {`Warning, you should fix ${StatusData[status.dataType]}`} - - - ))} -
- ); -} - -export function WidgetsComponent() { - const { config } = useConfig(); - - return ( - <> -
- - - - - - - {!window.__ANDROID__?.isThere() && } - -
-
- -
-
- -
- - {config?.debug && ( -
- -
- )} - - ); -} diff --git a/gui/src/components/commons/A.tsx b/gui/src/components/commons/A.tsx index 765200252..834f0abca 100644 --- a/gui/src/components/commons/A.tsx +++ b/gui/src/components/commons/A.tsx @@ -1,14 +1,23 @@ -import { openUrl } from '@tauri-apps/plugin-opener'; +import { open } from '@tauri-apps/plugin-shell'; +import classNames from 'classnames'; import { ReactNode } from 'react'; -export function A({ href, children }: { href?: string; children?: ReactNode }) { +export function A({ + href, + children, + className, +}: { + href?: string; + children?: ReactNode; + className?: string; +}) { return ( - href && openUrl(href).catch(() => window.open(href, '_blank')) + href && open(href).catch(() => window.open(href, '_blank')) } - className="underline" + className={classNames(className, 'underline', 'cursor-pointer')} > {children} diff --git a/gui/src/components/commons/Button.tsx b/gui/src/components/commons/Button.tsx index d87832388..1b4141cbb 100644 --- a/gui/src/components/commons/Button.tsx +++ b/gui/src/components/commons/Button.tsx @@ -2,6 +2,7 @@ import classNames from 'classnames'; import React, { ReactNode, useMemo } from 'react'; import { NavLink } from 'react-router-dom'; import { LoaderIcon, SlimeState } from './icon/LoaderIcon'; +import { Localized, LocalizedProps } from '@fluent/react'; function ButtonContent({ loading, @@ -17,11 +18,11 @@ function ButtonContent({
{icon && ( -
+
{icon}
)} @@ -44,7 +45,9 @@ export type ButtonProps = { loading?: boolean; rounded?: boolean; state?: any; -} & React.ButtonHTMLAttributes; + id?: string; +} & React.ButtonHTMLAttributes & + Omit; export function Button({ children, @@ -55,6 +58,10 @@ export function Button({ state = {}, icon, rounded = false, + attrs, + id, + vars, + elems, ...props }: ButtonProps) { const classes = useMemo(() => { @@ -95,7 +102,7 @@ export function Button({ ); }, [variant, disabled, rounded, props.className]); - return to ? ( + const content = to ? ( disabled && ev.preventDefault()} > - {children} + {id && ( + + {children} + + )} + {!id && children} ) : ( ); + + return content; } diff --git a/gui/src/components/commons/Checkbox.tsx b/gui/src/components/commons/Checkbox.tsx index 35d238115..bc76715d2 100644 --- a/gui/src/components/commons/Checkbox.tsx +++ b/gui/src/components/commons/Checkbox.tsx @@ -3,7 +3,7 @@ import { forwardRef, useMemo } from 'react'; import { Control, Controller } from 'react-hook-form'; export const CHECKBOX_CLASSES = classNames( - 'bg-background-50 border-background-50 rounded-md w-5 h-5 text-accent-background-30 focus:border-accent-background-40 focus:ring-transparent focus:ring-offset-transparent focus:outline-transparent' + 'bg-background-50 border-background-50 cursor-pointer rounded-md w-5 h-5 text-accent-background-30 focus:border-accent-background-40 focus:ring-transparent focus:ring-offset-transparent focus:outline-transparent' ); export const CheckboxInternal = forwardRef< @@ -34,7 +34,9 @@ export const CheckboxInternal = forwardRef< const classes = useMemo(() => { const vriantsMap = { checkbox: { - checkbox: CHECKBOX_CLASSES, + checkbox: classNames(CHECKBOX_CLASSES, { + 'brightness-50 hover:cursor-not-allowed': disabled, + }), toggle: '', pin: '', }, @@ -47,7 +49,7 @@ export const CheckboxInternal = forwardRef< }, }; return vriantsMap[variant]; - }, [variant]); + }, [variant, disabled]); return (
+
+
+ +
+
+ ); + + return showUpdate !== 'can-update' ? ( + + + + } + > +
{content}
+
+ ) : ( + + {content} + + ); +} + +export function FirmwareIcon({ + tracker, + device, +}: { + tracker: TrackerDataT; + device?: DeviceDataT; +}) { + const { currentFirmwareRelease } = useAppContext(); + + const showUpdate = + tracker.status !== TrackerStatusEnum.DISCONNECTED && + currentFirmwareRelease && + device && + checkForUpdate(currentFirmwareRelease, device); + + return ( +
+ {showUpdate && + showUpdate !== 'unavailable' && + showUpdate !== 'updated' && } +
+ ); +} diff --git a/gui/src/components/commons/ProgressBar.tsx b/gui/src/components/commons/ProgressBar.tsx index 85aeef24d..3e51932cd 100644 --- a/gui/src/components/commons/ProgressBar.tsx +++ b/gui/src/components/commons/ProgressBar.tsx @@ -53,8 +53,9 @@ export function Bar({ }) { const value = useMemo( () => Math.min(Math.max((progress * parts) / 1 - index, 0), 1), - [index, progress] + [index, progress, parts] ); + return (
const getFloatingTooltipPosition = ( preferedDirection: TooltipProps['preferedDirection'], + blockedDirections: Direction[], mode: TooltipProps['mode'], childrenRect: DOMRect, - tooltipRect: DOMRect + tooltipRect: DOMRect, + spacing: number ) => { - const spacing = 10; - const getPosition = ( direction: TooltipProps['preferedDirection'] ): TooltipPos => { @@ -135,9 +141,10 @@ const getFloatingTooltipPosition = ( const pos = getPosition(preferedDirection); if (isNotInside({ ...pos, height: tooltipRect.height }, windowRect)) { const [firstPos] = ['left', 'top', 'right', 'bottom'] + .filter((dir) => !blockedDirections.includes(dir as Direction)) .map((dir) => ({ dir, - area: getPosition(dir as TooltipProps['preferedDirection']), + area: getPosition(dir as Direction), })) .toSorted( (a, b) => @@ -226,12 +233,17 @@ const getFloatingTooltipPosition = ( export function FloatingTooltip({ childRef, preferedDirection, + blockedDirections = [], mode, children, + spacing, }: { - childRef: MutableRefObject; + childRef: MutableRefObject; children: ReactNode; -} & Pick) { +} & Pick< + TooltipProps, + 'mode' | 'preferedDirection' | 'blockedDirections' | 'spacing' +>) { const tooltipRef = useRef(null); const [tooltipStyle, setTooltipStyle] = useState(); @@ -245,9 +257,11 @@ export function FloatingTooltip({ setTooltipStyle( getFloatingTooltipPosition( preferedDirection, + blockedDirections, mode, childrenRect, - tooltipRect + tooltipRect, + spacing ?? 20 ) ); }; @@ -314,7 +328,7 @@ export function DrawerTooltip({ childRef, }: { children: ReactNode; - childRef: MutableRefObject; + childRef: MutableRefObject; }) { const touchTimestamp = useRef(0); const touchTimeout = useRef(0); @@ -377,17 +391,12 @@ export function DrawerTooltip({ if (childRef.current && childRef.current.children[0]) { const elem = childRef.current.children[0] as HTMLElement; - elem.addEventListener('mousedown', touchStart); // for debug on desktop - elem.addEventListener('mouseup', touchEnd); // for debug on desktop elem.addEventListener('scroll', scroll); - elem.addEventListener('click', touchEnd); elem.addEventListener('touchstart', touchStart); elem.addEventListener('touchend', touchEnd); return () => { - elem.removeEventListener('mousedown', touchStart); // for debug on desktop - elem.removeEventListener('mouseup', touchEnd); // for debug on desktop elem.removeEventListener('scroll', scroll); elem.removeEventListener('touchstart', touchStart); @@ -396,6 +405,7 @@ export function DrawerTooltip({ }; } }, []); + // FIXME: Completely broken not sure why. Will be solved when tooltips on mobile actually work return ( <> @@ -444,33 +454,53 @@ export function Tooltip({ content, children, preferedDirection, + blockedDirections = [], mode = 'center', + variant = 'auto', disabled = false, + tag = 'div', + spacing = 20, }: TooltipProps) { - const childRef = useRef(null); + const childRef = useRef(null); const { isMobile } = useBreakpoint('mobile'); - const portal = createPortal( - isMobile ? ( + let portal = null; + if (variant === 'auto') { + portal = isMobile ? ( {content} ) : ( {content} - ), - document.body - ); + ); + } + + if (variant === 'drawer') + portal = {content}; + + if (variant === 'floating') + portal = ( + + {content} + + ); return ( <> -
- {children} -
- {!disabled && portal} + {createElement(tag, { className: 'contents', ref: childRef }, children)} + {!disabled && createPortal(portal, document.body)} ); } diff --git a/gui/src/components/commons/Typography.tsx b/gui/src/components/commons/Typography.tsx index fb1917ede..03de1ea73 100644 --- a/gui/src/components/commons/Typography.tsx +++ b/gui/src/components/commons/Typography.tsx @@ -1,4 +1,5 @@ import { useConfig } from '@/hooks/config'; +import { Localized, LocalizedProps } from '@fluent/react'; import classNames from 'classnames'; import { createElement, ReactNode, useMemo } from 'react'; @@ -12,6 +13,10 @@ export function Typography({ truncate = false, textAlign, sentryMask = false, + id, + attrs, + elems, + vars, }: { variant?: | 'main-title' @@ -39,7 +44,8 @@ export function Typography({ | 'text-end'; children?: ReactNode; sentryMask?: boolean; -}) { + id?: string; +} & Omit) { const tag = useMemo(() => { const tags = { 'main-title': 'h1', @@ -52,7 +58,7 @@ export function Typography({ }, [variant]); const { config } = useConfig(); - return createElement( + const element = createElement( tag, { className: classNames([ @@ -71,12 +77,22 @@ export function Typography({ whitespace, textAlign, italic && 'italic', - truncate && 'leading-3 text-ellipsis', + truncate && 'leading-[1.2rem] text-ellipsis', truncate && (config?.textSize ?? 12) > 12 && 'line-clamp-1', truncate && (config?.textSize ?? 12) <= 12 && 'line-clamp-2', sentryMask && 'sentry-mask', ]), }, - children || [] + children || id || [] ); + + if (id) { + return ( + + {element} + + ); + } + + return element; } diff --git a/gui/src/components/commons/icon/ChecklistIcon.tsx b/gui/src/components/commons/icon/ChecklistIcon.tsx new file mode 100644 index 000000000..e710a40b3 --- /dev/null +++ b/gui/src/components/commons/icon/ChecklistIcon.tsx @@ -0,0 +1,12 @@ +export function Checklist({ size = 24 }: { size?: number }) { + return ( + + + + ); +} diff --git a/gui/src/components/commons/icon/ClearIcon.tsx b/gui/src/components/commons/icon/ClearIcon.tsx new file mode 100644 index 000000000..85bbde08d --- /dev/null +++ b/gui/src/components/commons/icon/ClearIcon.tsx @@ -0,0 +1,12 @@ +export function ClearIcon({ size = 24 }: { size?: number }) { + return ( + + + + ); +} diff --git a/gui/src/components/commons/icon/GearIcon.tsx b/gui/src/components/commons/icon/GearIcon.tsx index 668ea636b..87a46a1b3 100644 --- a/gui/src/components/commons/icon/GearIcon.tsx +++ b/gui/src/components/commons/icon/GearIcon.tsx @@ -1,8 +1,8 @@ -export function GearIcon() { +export function GearIcon({ size = 20 }: { size?: number }) { return ( diff --git a/gui/src/components/commons/icon/HomeIcon.tsx b/gui/src/components/commons/icon/HomeIcon.tsx new file mode 100644 index 000000000..18d4f03df --- /dev/null +++ b/gui/src/components/commons/icon/HomeIcon.tsx @@ -0,0 +1,12 @@ +export function HomeIcon() { + return ( + + + + ); +} diff --git a/gui/src/components/commons/icon/LayoutIcon.tsx b/gui/src/components/commons/icon/LayoutIcon.tsx new file mode 100644 index 000000000..a82a78480 --- /dev/null +++ b/gui/src/components/commons/icon/LayoutIcon.tsx @@ -0,0 +1,12 @@ +export function LayoutIcon({ size = 16 }: { size?: number }) { + return ( + + + + ); +} diff --git a/gui/src/components/commons/icon/SkiIcon.tsx b/gui/src/components/commons/icon/SkiIcon.tsx new file mode 100644 index 000000000..e6dee6626 --- /dev/null +++ b/gui/src/components/commons/icon/SkiIcon.tsx @@ -0,0 +1,12 @@ +export function SkiIcon({ size = 24 }: { size?: number }) { + return ( + + + + ); +} diff --git a/gui/src/components/home/Home.tsx b/gui/src/components/home/Home.tsx index b5a9898c3..1243811b8 100644 --- a/gui/src/components/home/Home.tsx +++ b/gui/src/components/home/Home.tsx @@ -1,31 +1,28 @@ -import { Localized, useLocalization } from '@fluent/react'; -import { Link, NavLink, useNavigate } from 'react-router-dom'; -import { StatusData, TrackerDataT } from 'solarxr-protocol'; +import { useLocalization } from '@fluent/react'; +import { NavLink, useNavigate } from 'react-router-dom'; +import { TrackerDataT } from 'solarxr-protocol'; import { useConfig } from '@/hooks/config'; import { Typography } from '@/components/commons/Typography'; import { TrackerCard } from '@/components/tracker/TrackerCard'; import { TrackersTable } from '@/components/tracker/TrackersTable'; -import { - parseStatusToLocale, - trackerStatusRelated, - useStatusContext, -} from '@/hooks/status-system'; -import { useMemo } from 'react'; -import { WarningBox } from '@/components/commons/TipBox'; import { HeadsetIcon } from '@/components/commons/icon/HeadsetIcon'; -import classNames from 'classnames'; import { useAtomValue } from 'jotai'; -import { flatTrackersAtom } from '@/store/app-store'; -import { useVRCConfig } from '@/hooks/vrc-config'; - -const DONT_REPEAT_STATUSES = [StatusData.StatusTrackerReset]; +import { + assignedTrackersAtom, + unassignedTrackersAtom, +} from '@/store/app-store'; +import { useTrackingChecklist } from '@/hooks/tracking-checklist'; +import { Checklist } from '@/components/commons/icon/ChecklistIcon'; +import { useState } from 'react'; +import { HomeSettingsModal } from './HomeSettingsModal'; +import { LayoutIcon } from '@/components/commons/icon/LayoutIcon'; export function Home() { const { l10n } = useLocalization(); const { config } = useConfig(); - const trackers = useAtomValue(flatTrackersAtom); - const { statuses } = useStatusContext(); - const { invalidConfig } = useVRCConfig(); + const trackers = useAtomValue(assignedTrackersAtom); + const unassignedTrackers = useAtomValue(unassignedTrackersAtom); + const { highlightedTrackers } = useTrackingChecklist(); const navigate = useNavigate(); const sendToSettings = (tracker: TrackerDataT) => { @@ -34,95 +31,121 @@ export function Home() { ); }; - const filteredStatuses = useMemo(() => { - const dontRepeat = new Map(DONT_REPEAT_STATUSES.map((x) => [x, false])); - return Object.entries(statuses).filter(([, value]) => { - if (dontRepeat.get(value.dataType)) return false; - if (dontRepeat.has(value.dataType)) dontRepeat.set(value.dataType, true); - return true; - }); - }, [statuses]); + const settingsOpenState = useState(false); + const [, setSettingsOpen] = settingsOpenState; return (
+ -
-
- {filteredStatuses - .filter(([, status]) => status.prioritized) - .map(([, status]) => ( - - - {`Warning, you should fix ${StatusData[status.dataType]}`} - - - ))} - {invalidConfig && ( - -
-
- -
-
- -
- -
- -
-
-
- )} + + + +
+
+ +
+
setSettingsOpen(true)} + > + +
-
- {trackers.length === 0 && ( -
- - {l10n.getString('home-no_trackers')} - -
- )} + {trackers.length === 0 && ( +
+ + {l10n.getString('home-no_trackers')} + +
+ )} - {!config?.debug && trackers.length > 0 && ( -
- {trackers.map(({ tracker, device }, index) => ( - sendToSettings(tracker)} - smol - showUpdates - interactable - warning={Object.values(statuses).some((status) => - trackerStatusRelated(tracker, status) - )} - /> - ))} -
- )} - {config?.debug && trackers.length > 0 && ( -
- sendToSettings(tracker)} + {config?.homeLayout == 'default' && trackers.length > 0 && ( +
+ {trackers.map(({ tracker, device }, index) => ( + sendToSettings(tracker)} + smol + showUpdates + interactable + warning={ + !!highlightedTrackers?.trackers.find( + (t) => + t?.deviceId?.id === tracker.trackerId?.deviceId?.id && + t?.trackerNum === tracker.trackerId?.trackerNum + ) && highlightedTrackers.step + } /> + ))} +
+ )} + + {config?.homeLayout === 'table' && trackers.length > 0 && ( +
+ sendToSettings(tracker)} + /> +
+ )} + + {unassignedTrackers.length > 0 && ( + <> +
+ +
- )} -
+ {config?.homeLayout == 'default' && ( +
+ {unassignedTrackers.map(({ tracker, device }, index) => ( + sendToSettings(tracker)} + smol + showUpdates + interactable + warning={ + !!highlightedTrackers?.trackers.find( + (t) => + t?.deviceId?.id === tracker.trackerId?.deviceId?.id && + t?.trackerNum === tracker.trackerId?.trackerNum + ) && highlightedTrackers.step + } + /> + ))} +
+ )} + {config?.homeLayout === 'table' && ( +
+ sendToSettings(tracker)} + /> +
+ )} + + )}
); diff --git a/gui/src/components/home/HomeSettingsModal.tsx b/gui/src/components/home/HomeSettingsModal.tsx new file mode 100644 index 000000000..dc269930c --- /dev/null +++ b/gui/src/components/home/HomeSettingsModal.tsx @@ -0,0 +1,34 @@ +import { Dispatch, SetStateAction } from 'react'; +import { BaseModal } from '@/components/commons/BaseModal'; +import { Typography } from '@/components/commons/Typography'; +import { Button } from '@/components/commons/Button'; +import { HomeLayoutSettings } from '@/components/settings/pages/HomeScreenSettings'; + +export function HomeSettingsModal({ + open, +}: { + open: [boolean, Dispatch>]; +}) { + return ( + { + open[1](false); + }} + > +
+ + +
+
+
+
+ ); +} diff --git a/gui/src/components/home/ResetButton.tsx b/gui/src/components/home/ResetButton.tsx index 78d1c7fda..e3901a890 100644 --- a/gui/src/components/home/ResetButton.tsx +++ b/gui/src/components/home/ResetButton.tsx @@ -1,219 +1,67 @@ -import { useLocalization } from '@fluent/react'; -import { useEffect, useMemo, useRef, useState } from 'react'; -import { - BodyPart, - ResetRequestT, - ResetType, - RpcMessage, - StatusData, -} from 'solarxr-protocol'; -import { useConfig } from '@/hooks/config'; -import { useCountdown } from '@/hooks/countdown'; -import { useWebsocketAPI } from '@/hooks/websocket-api'; -import { - playSoundOnResetEnded, - playSoundOnResetStarted, -} from '@/sounds/sounds'; -import { BigButton } from '@/components/commons/BigButton'; +import { Localized } from '@fluent/react'; +import { ResetType } from 'solarxr-protocol'; import { Button } from '@/components/commons/Button'; -import { - MountingResetIcon, - YawResetIcon, - FullResetIcon, -} from '@/components/commons/icon/ResetIcon'; -import { useStatusContext } from '@/hooks/status-system'; import classNames from 'classnames'; +import { useReset, UseResetOptions } from '@/hooks/reset'; +import { + FullResetIcon, + YawResetIcon, +} from '@/components/commons/icon/ResetIcon'; +import { ReactNode } from 'react'; +import { SkiIcon } from '@/components/commons/icon/SkiIcon'; import { FootIcon } from '@/components/commons/icon/FootIcon'; import { FingersIcon } from '@/components/commons/icon/FingersIcon'; +export function ResetButtonIcon(options: UseResetOptions) { + if (options.type === ResetType.Mounting && !options.group) + options.group = 'default'; + + if (options.type === ResetType.Yaw) return ; + if (options.type === ResetType.Full) return ; + if (options.type === ResetType.Mounting) { + if (options.group === 'default') return ; + if (options.group === 'feet') return ; + if (options.group === 'fingers') return ; + } +} + export function ResetButton({ - type, - size = 'big', - bodyPartsToReset = 'default', className, onReseted, + children, + ...options }: { className?: string; - type: ResetType; - size: 'big' | 'small'; - bodyPartsToReset?: 'default' | 'feet' | 'fingers'; + children?: ReactNode; onReseted?: () => void; -}) { - const { l10n } = useLocalization(); - const { sendRPCPacket } = useWebsocketAPI(); - const { statuses } = useStatusContext(); - const { config } = useConfig(); - const finishedTimeoutRef = useRef(-1); - const [isFinished, setFinished] = useState(false); - - const needsFullReset = useMemo( - () => - type === ResetType.Mounting && - Object.values(statuses).some( - (status) => status.dataType === StatusData.StatusTrackerReset - ), - [statuses] +} & UseResetOptions) { + const { triggerReset, status, timer, disabled, name } = useReset( + options, + onReseted ); - const feetBodyParts = [BodyPart.LEFT_FOOT, BodyPart.RIGHT_FOOT]; - const fingerBodyParts = [ - BodyPart.LEFT_THUMB_METACARPAL, - BodyPart.LEFT_THUMB_PROXIMAL, - BodyPart.LEFT_THUMB_DISTAL, - BodyPart.LEFT_INDEX_PROXIMAL, - BodyPart.LEFT_INDEX_INTERMEDIATE, - BodyPart.LEFT_INDEX_DISTAL, - BodyPart.LEFT_MIDDLE_PROXIMAL, - BodyPart.LEFT_MIDDLE_INTERMEDIATE, - BodyPart.LEFT_MIDDLE_DISTAL, - BodyPart.LEFT_RING_PROXIMAL, - BodyPart.LEFT_RING_INTERMEDIATE, - BodyPart.LEFT_RING_DISTAL, - BodyPart.LEFT_LITTLE_PROXIMAL, - BodyPart.LEFT_LITTLE_INTERMEDIATE, - BodyPart.LEFT_LITTLE_DISTAL, - BodyPart.RIGHT_THUMB_METACARPAL, - BodyPart.RIGHT_THUMB_PROXIMAL, - BodyPart.RIGHT_THUMB_DISTAL, - BodyPart.RIGHT_INDEX_PROXIMAL, - BodyPart.RIGHT_INDEX_INTERMEDIATE, - BodyPart.RIGHT_INDEX_DISTAL, - BodyPart.RIGHT_MIDDLE_PROXIMAL, - BodyPart.RIGHT_MIDDLE_INTERMEDIATE, - BodyPart.RIGHT_MIDDLE_DISTAL, - BodyPart.RIGHT_RING_PROXIMAL, - BodyPart.RIGHT_RING_INTERMEDIATE, - BodyPart.RIGHT_RING_DISTAL, - BodyPart.RIGHT_LITTLE_PROXIMAL, - BodyPart.RIGHT_LITTLE_INTERMEDIATE, - BodyPart.RIGHT_LITTLE_DISTAL, - ]; - - const reset = () => { - const req = new ResetRequestT(); - req.resetType = type; - switch (bodyPartsToReset) { - case 'default': - // Server handles it. Usually all body parts except fingers. - req.bodyParts = []; - break; - case 'feet': - req.bodyParts = feetBodyParts; - break; - case 'fingers': - req.bodyParts = fingerBodyParts; - break; - } - sendRPCPacket(RpcMessage.ResetRequest, req); - }; - - const { isCounting, startCountdown, timer } = useCountdown({ - duration: type === ResetType.Yaw ? 0 : undefined, - onCountdownEnd: () => { - maybePlaySoundOnResetEnd(type); - reset(); - setFinished(true); - if (finishedTimeoutRef.current !== -1) - clearTimeout(finishedTimeoutRef.current); - finishedTimeoutRef.current = setTimeout(() => { - setFinished(false); - finishedTimeoutRef.current = -1; - }, 2000) as unknown as number; - if (onReseted) onReseted(); - }, - }); - - const text = useMemo(() => { - switch (type) { - case ResetType.Yaw: - return l10n.getString( - 'reset-yaw' + - (bodyPartsToReset !== 'default' ? '-' + bodyPartsToReset : '') - ); - case ResetType.Mounting: - return l10n.getString( - 'reset-mounting' + - (bodyPartsToReset !== 'default' ? '-' + bodyPartsToReset : '') - ); - case ResetType.Full: - return l10n.getString( - 'reset-full' + - (bodyPartsToReset !== 'default' ? '-' + bodyPartsToReset : '') - ); - } - }, [type, bodyPartsToReset]); - - const getIcon = () => { - switch (type) { - case ResetType.Yaw: - return ; - case ResetType.Mounting: - switch (bodyPartsToReset) { - case 'default': - return ; - case 'feet': - return ; - case 'fingers': - return ; - } - } - return ; - }; - - const maybePlaySoundOnResetEnd = (type: ResetType) => { - if (!config?.feedbackSound) return; - playSoundOnResetEnded(type, config?.feedbackSoundVolume); - }; - - const maybePlaySoundOnResetStart = () => { - if (!config?.feedbackSound) return; - if (type !== ResetType.Yaw) - playSoundOnResetStarted(config?.feedbackSoundVolume); - }; - - const triggerReset = () => { - setFinished(false); - startCountdown(); - maybePlaySoundOnResetStart(); - }; - - useEffect(() => { - return () => { - if (finishedTimeoutRef.current !== -1) - clearTimeout(finishedTimeoutRef.current); - }; - }, []); - - return size === 'small' ? ( + return ( - ) : ( - - {!isCounting || type === ResetType.Yaw ? text : String(timer)} - ); } diff --git a/gui/src/components/onboarding/OnboardingLayout.tsx b/gui/src/components/onboarding/OnboardingLayout.tsx index 64501ac9c..46ee15aa2 100644 --- a/gui/src/components/onboarding/OnboardingLayout.tsx +++ b/gui/src/components/onboarding/OnboardingLayout.tsx @@ -34,8 +34,6 @@ export function OnboardingLayout({ children }: { children: ReactNode }) {
) : ( - - {children} - + {children} ); } diff --git a/gui/src/components/onboarding/pages/ConnectTracker.scss b/gui/src/components/onboarding/pages/ConnectTracker.scss index 36acf8219..c44a97333 100644 --- a/gui/src/components/onboarding/pages/ConnectTracker.scss +++ b/gui/src/components/onboarding/pages/ConnectTracker.scss @@ -12,9 +12,9 @@ grid-template: 's t' var(--connect-tracker-layout-top) 's c' calc(100% - var(--connect-tracker-layout-top)) - / calc(var(--connect-tracker-layout-sidebar)) calc( - 100% - var(--connect-tracker-layout-sidebar) - ); + / calc(var(--connect-tracker-layout-sidebar)) calc(100% - var( + --connect-tracker-layout-sidebar + )); @screen mobile { grid-template: diff --git a/gui/src/components/onboarding/pages/ConnectTracker.tsx b/gui/src/components/onboarding/pages/ConnectTracker.tsx index 9933454f1..1b22b178b 100644 --- a/gui/src/components/onboarding/pages/ConnectTracker.tsx +++ b/gui/src/components/onboarding/pages/ConnectTracker.tsx @@ -5,8 +5,9 @@ import { useNavigate } from 'react-router-dom'; import { RpcMessage, StartWifiProvisioningRequestT, - StatusData, StopWifiProvisioningRequestT, + TrackingChecklistPublicNetworksT, + TrackingChecklistStepId, WifiProvisioningStatus, WifiProvisioningStatusResponseT, } from 'solarxr-protocol'; @@ -24,9 +25,9 @@ import './ConnectTracker.scss'; import { useAtomValue } from 'jotai'; import { connectedIMUTrackersAtom } from '@/store/app-store'; import { BaseModal } from '@/components/commons/BaseModal'; -import { parseStatusToLocale, useStatusContext } from '@/hooks/status-system'; import { A } from '@/components/commons/A'; import { CONNECT_TRACKER } from '@/utils/tauri'; +import { useTrackingChecklist } from '@/hooks/tracking-checklist'; const statusLabelMap = { [WifiProvisioningStatus.NONE]: @@ -74,9 +75,34 @@ const statusProgressMap = { [WifiProvisioningStatus.COULD_NOT_FIND_SERVER]: 0.8, }; +export function InvalidNetworkProfileWarning({ + extraData, +}: { + extraData: TrackingChecklistPublicNetworksT; +}) { + return ( +
+ + ), + }} + vars={{ + count: extraData.adapters.length, + adapters: extraData.adapters.join(', '), + }} + > + WARNING + +
+ ); +} + export function ConnectTrackersPage() { const { l10n } = useLocalization(); - const { statuses } = useStatusContext(); + const { visibleSteps } = useTrackingChecklist(); const connectedIMUTrackers = useAtomValue(connectedIMUTrackersAtom); const { applyProgress, state } = useOnboarding(); @@ -165,11 +191,13 @@ export function ConnectTrackersPage() { [connectedIMUTrackers.length] ); - const filteredStatuses = useMemo(() => { - return Object.entries(statuses).filter( - ([, value]) => value.dataType == StatusData.StatusPublicNetwork + const invalidNetworkProfile = useMemo(() => { + return visibleSteps.find( + (step) => + step.id === TrackingChecklistStepId.NETWORK_PROFILE_PUBLIC && + !step.valid ); - }, [statuses]); + }, [visibleSteps]); return ( <> @@ -243,24 +271,13 @@ export function ConnectTrackersPage() { > Conditional tip - {filteredStatuses.map(([, status]) => ( -
- - ), - }} - > - - {`Warning, you should fix ${StatusData[status.dataType]}`} - - -
- ))} + {invalidNetworkProfile && ( + + )}
diff --git a/gui/src/components/onboarding/pages/body-proportions/autobone-steps/Preparation.tsx b/gui/src/components/onboarding/pages/body-proportions/autobone-steps/Preparation.tsx index 5d7c58d06..7dd87b6ae 100644 --- a/gui/src/components/onboarding/pages/body-proportions/autobone-steps/Preparation.tsx +++ b/gui/src/components/onboarding/pages/body-proportions/autobone-steps/Preparation.tsx @@ -69,11 +69,7 @@ export function PreparationStep({ > {l10n.getString('onboarding-automatic_mounting-prev_step')} - +
diff --git a/gui/src/components/onboarding/pages/mounting/mounting-steps/MountingReset.tsx b/gui/src/components/onboarding/pages/mounting/mounting-steps/MountingReset.tsx index 5edcb13c6..1943d757e 100644 --- a/gui/src/components/onboarding/pages/mounting/mounting-steps/MountingReset.tsx +++ b/gui/src/components/onboarding/pages/mounting/mounting-steps/MountingReset.tsx @@ -58,8 +58,8 @@ export function MountingResetStep({ {l10n.getString('onboarding-automatic_mounting-prev_step')}
diff --git a/gui/src/components/onboarding/pages/mounting/mounting-steps/Preparation.tsx b/gui/src/components/onboarding/pages/mounting/mounting-steps/Preparation.tsx index b4689779e..bc9ca3b7c 100644 --- a/gui/src/components/onboarding/pages/mounting/mounting-steps/Preparation.tsx +++ b/gui/src/components/onboarding/pages/mounting/mounting-steps/Preparation.tsx @@ -69,11 +69,7 @@ export function PreparationStep({ > {l10n.getString('onboarding-automatic_mounting-prev_step')} - +
diff --git a/gui/src/components/onboarding/pages/stay-aligned/stay-aligned-steps/PreparationStep.tsx b/gui/src/components/onboarding/pages/stay-aligned/stay-aligned-steps/PreparationStep.tsx index 06097a7f8..78a6c5abe 100644 --- a/gui/src/components/onboarding/pages/stay-aligned/stay-aligned-steps/PreparationStep.tsx +++ b/gui/src/components/onboarding/pages/stay-aligned/stay-aligned-steps/PreparationStep.tsx @@ -53,7 +53,7 @@ export function PreparationStep({
); diff --git a/gui/src/components/onboarding/pages/stay-aligned/stay-aligned-steps/VerifyMounting.tsx b/gui/src/components/onboarding/pages/stay-aligned/stay-aligned-steps/VerifyMounting.tsx index 03c983f52..59d3678d8 100644 --- a/gui/src/components/onboarding/pages/stay-aligned/stay-aligned-steps/VerifyMounting.tsx +++ b/gui/src/components/onboarding/pages/stay-aligned/stay-aligned-steps/VerifyMounting.tsx @@ -54,8 +54,8 @@ export function VerifyMountingStep({ {l10n.getString('onboarding-automatic_mounting-prev_step')}
diff --git a/gui/src/components/onboarding/pages/trackers-assign/TrackerAssignment.tsx b/gui/src/components/onboarding/pages/trackers-assign/TrackerAssignment.tsx index b84ecece5..c3a20dd9a 100644 --- a/gui/src/components/onboarding/pages/trackers-assign/TrackerAssignment.tsx +++ b/gui/src/components/onboarding/pages/trackers-assign/TrackerAssignment.tsx @@ -281,7 +281,7 @@ export function TrackersAssignPage() {
- + {l10n.getString('onboarding-assign_trackers-title')} diff --git a/gui/src/components/providers/StatusSystemContext.tsx b/gui/src/components/providers/StatusSystemContext.tsx deleted file mode 100644 index f04bf420b..000000000 --- a/gui/src/components/providers/StatusSystemContext.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { ReactNode } from 'react'; -import { StatusSystemC, useProvideStatusContext } from '@/hooks/status-system'; - -export function StatusProvider({ children }: { children: ReactNode }) { - const context = useProvideStatusContext(); - - return ( - {children} - ); -} diff --git a/gui/src/components/settings/SettingsLayout.scss b/gui/src/components/settings/SettingsLayout.scss index 3cc388710..eb70bef5e 100644 --- a/gui/src/components/settings/SettingsLayout.scss +++ b/gui/src/components/settings/SettingsLayout.scss @@ -7,9 +7,9 @@ grid-template: 't t t' var(--topbar-h) 'n s c' calc(100% - var(--topbar-h)) - / var(--navbar-w) var(--settings-sidebar-w) calc( - 100% - var(--navbar-w) - var(--settings-sidebar-w) - ); + / var(--navbar-w) var(--settings-sidebar-w) calc(100% - var(--navbar-w) - var( + --settings-sidebar-w + )); @screen lg { --settings-sidebar-w: 270px; diff --git a/gui/src/components/settings/SettingsPageLayout.tsx b/gui/src/components/settings/SettingsPageLayout.tsx index 29329894f..d69057ca4 100644 --- a/gui/src/components/settings/SettingsPageLayout.tsx +++ b/gui/src/components/settings/SettingsPageLayout.tsx @@ -64,7 +64,7 @@ export function SettingsPagePaneLayout({ {...props} >
-
+
{icon}
diff --git a/gui/src/components/settings/SettingsSidebar.tsx b/gui/src/components/settings/SettingsSidebar.tsx index ffb09640a..3628c55e6 100644 --- a/gui/src/components/settings/SettingsSidebar.tsx +++ b/gui/src/components/settings/SettingsSidebar.tsx @@ -1,18 +1,17 @@ import classNames from 'classnames'; -import { ReactNode, useMemo } from 'react'; +import { useMemo } from 'react'; import { NavLink, useLocation, useMatch } from 'react-router-dom'; import { Typography } from '@/components/commons/Typography'; -import { useLocalization } from '@fluent/react'; import { useVRCConfig } from '@/hooks/vrc-config'; export function SettingsLink({ to, scrollTo, - children, + id, }: { + id: string; to: string; scrollTo?: string; - children: ReactNode; }) { const { state } = useLocation(); const doesMatch = useMatch({ @@ -35,94 +34,118 @@ export function SettingsLink({ 'bg-background-60': isActive, })} > - {children} + ); } export function SettingsSidebar() { - const { l10n } = useLocalization(); const { state: vrcConfigState } = useVRCConfig(); return (
- - {l10n.getString('settings-sidebar-title')} - +
- - {l10n.getString('settings-sidebar-general')} - +
- - SteamVR - - - {l10n.getString('settings-sidebar-stay_aligned')} - - - {l10n.getString('settings-sidebar-tracker_mechanics')} - - - {l10n.getString('settings-sidebar-fk_settings')} - - - {l10n.getString('settings-sidebar-gesture_control')} - + + + + +
- - {l10n.getString('settings-sidebar-interface')} - +
- - {l10n.getString('settings-sidebar-notifications')} - - - {l10n.getString('settings-sidebar-behavior')} - - - {l10n.getString('settings-sidebar-appearance')} - + + + + +
OSC
- - {l10n.getString('settings-sidebar-osc_router')} - - - {l10n.getString('settings-sidebar-osc_trackers')} - - - VMC - + + +
- - {l10n.getString('settings-sidebar-utils')} - +
- - {l10n.getString('settings-sidebar-serial')} - - - {l10n.getString('settings-sidebar-firmware-tool')} - + +
{vrcConfigState?.isSupported && (
- - {l10n.getString('settings-sidebar-vrc_warnings')} - +
)}
- - {l10n.getString('settings-sidebar-advanced')} - +
diff --git a/gui/src/components/settings/pages/GeneralSettings.tsx b/gui/src/components/settings/pages/GeneralSettings.tsx index 4d4f5694c..d55478deb 100644 --- a/gui/src/components/settings/pages/GeneralSettings.tsx +++ b/gui/src/components/settings/pages/GeneralSettings.tsx @@ -9,7 +9,6 @@ import { ModelRatiosT, ModelSettingsT, ModelTogglesT, - ResetsSettingsT, RpcMessage, SettingsRequestT, SettingsResponseT, @@ -38,6 +37,11 @@ import { serializeStayAlignedSettings, deserializeStayAlignedSettings, } from './components/StayAlignedSettings'; +import { + defaultResetSettings, + loadResetSettings, + ResetSettingsForm, +} from '@/hooks/reset-settings'; export type SettingsForm = { trackers: { @@ -95,13 +99,7 @@ export type SettingsForm = { legTweaks: { correctionStrength: number; }; - resetsSettings: { - resetMountingFeet: boolean; - armsMountingResetMode: number; - yawResetSmoothTime: number; - saveMountingReset: boolean; - resetHmdPitch: boolean; - }; + resetsSettings: ResetSettingsForm; stayAligned: StayAlignedSettingsForm; }; @@ -156,22 +154,14 @@ const defaultValues: SettingsForm = { numberTrackersOverThreshold: 1, }, legTweaks: { correctionStrength: 0.3 }, - resetsSettings: { - resetMountingFeet: false, - armsMountingResetMode: 0, - yawResetSmoothTime: 0.0, - saveMountingReset: false, - resetHmdPitch: false, - }, + resetsSettings: defaultResetSettings, stayAligned: defaultStayAlignedSettings, }; export function GeneralSettings() { const { l10n } = useLocalization(); const { config } = useConfig(); - // const { state } = useLocation(); const { currentLocales } = useLocaleConfig(); - // const pageRef = useRef(null); const percentageFormat = new Intl.NumberFormat(currentLocales, { style: 'percent', @@ -288,17 +278,7 @@ export function GeneralSettings() { settings.stayAligned = serializeStayAlignedSettings(values.stayAligned); if (values.resetsSettings) { - const resetsSettings = new ResetsSettingsT(); - resetsSettings.resetMountingFeet = - values.resetsSettings.resetMountingFeet; - resetsSettings.armsMountingResetMode = - values.resetsSettings.armsMountingResetMode; - resetsSettings.yawResetSmoothTime = - values.resetsSettings.yawResetSmoothTime; - resetsSettings.saveMountingReset = - values.resetsSettings.saveMountingReset; - resetsSettings.resetHmdPitch = values.resetsSettings.resetHmdPitch; - settings.resetsSettings = resetsSettings; + settings.resetsSettings = loadResetSettings(values.resetsSettings); } sendRPCPacket(RpcMessage.ChangeSettingsRequest, settings); diff --git a/gui/src/components/settings/pages/HomeScreenSettings.tsx b/gui/src/components/settings/pages/HomeScreenSettings.tsx new file mode 100644 index 000000000..483202db8 --- /dev/null +++ b/gui/src/components/settings/pages/HomeScreenSettings.tsx @@ -0,0 +1,190 @@ +import { CheckBox } from '@/components/commons/Checkbox'; +import { CheckIcon } from '@/components/commons/icon/CheckIcon'; +import { HomeIcon } from '@/components/commons/icon/HomeIcon'; +import { Typography } from '@/components/commons/Typography'; +import { + SettingsPageLayout, + SettingsPagePaneLayout, +} from '@/components/settings/SettingsPageLayout'; +import { Config, useConfig } from '@/hooks/config'; +import { + trackingchecklistIdtoLabel, + useTrackingChecklist, +} from '@/hooks/tracking-checklist'; +import { useLocalization } from '@fluent/react'; +import classNames from 'classnames'; +import { ReactNode, useEffect } from 'react'; +import { useForm } from 'react-hook-form'; +import { TrackingChecklistStepId } from 'solarxr-protocol'; + +type StepsForm = { steps: Record }; +export function TrackingChecklistSettings({ + variant, +}: { + variant: 'settings' | 'modal'; +}) { + const { l10n } = useLocalization(); + const { ignoredSteps, steps, ignoreStep } = useTrackingChecklist(); + + const { control, reset, handleSubmit } = useForm({ + defaultValues: { + steps: steps.reduce( + (curr, { id }) => ({ [id]: !ignoredSteps.includes(id), ...curr }), + {} + ), + }, + mode: 'onChange', + }); + + useEffect(() => { + reset({ + steps: steps.reduce( + (curr, { id }) => ({ [id]: !ignoredSteps.includes(id), ...curr }), + {} + ), + }); + }, [ignoredSteps]); + + const onSubmit = (values: StepsForm) => { + for (const [id, value] of Object.entries(values.steps)) { + const stepId = +id; + if (!stepId) continue; + + // doing it this way prevents calling ignore step for every step. + // that prevent sending a packet for steps that didnt change + if (!value && !ignoredSteps.includes(stepId)) { + ignoreStep(stepId, true); + } + + if (value && ignoredSteps.includes(stepId)) { + ignoreStep(stepId, false); + } + } + }; + + return ( +
+
+ + +
+
+ {steps + .filter((step) => step.enabled) + .map((step) => ( +
+ +
+ ))} +
+
+ ); +} + +export function LayoutSelector({ + children, + name, + active = false, + onClick, +}: { + children: ReactNode; + name: string; + active: boolean; + onClick: () => void; +}) { + return ( +
+
+ +
+
+ {children} +
+ ); +} + +export function HomeLayoutSettings() { + const { config, setConfig } = useConfig(); + + const setLayout = (layout: Config['homeLayout']) => + setConfig({ homeLayout: layout }); + + return ( +
+
+ + +
+
+ setLayout('default')} + > +
+
+
+
+
+
+ + setLayout('table')} + > +
+
+
+
+
+
+ +
+
+ ); +} + +export function HomeScreenSettings() { + return ( + +
+ }> + + + + }> + + + +
+
+ ); +} diff --git a/gui/src/components/settings/pages/InterfaceSettings.tsx b/gui/src/components/settings/pages/InterfaceSettings.tsx index 67a780270..126710a88 100644 --- a/gui/src/components/settings/pages/InterfaceSettings.tsx +++ b/gui/src/components/settings/pages/InterfaceSettings.tsx @@ -20,6 +20,7 @@ import { ArrowRightLeftIcon } from '@/components/commons/icon/ArrowIcons'; import { isTrayAvailable } from '@/utils/tauri'; import { isTauri } from '@tauri-apps/api/core'; import { TauriFileInput } from '@/components/commons/TauriFileInput'; +import { DeveloperModeWidget } from '@/components/widgets/DeveloperModeWidget'; interface InterfaceSettingsForm { appearance: { @@ -304,16 +305,19 @@ export function InterfaceSettings() { )}
-
- +
+
+ +
+ {config?.debug && }
diff --git a/gui/src/components/tracker/TrackerCard.tsx b/gui/src/components/tracker/TrackerCard.tsx index 9fd3bd0c7..00dafabe0 100644 --- a/gui/src/components/tracker/TrackerCard.tsx +++ b/gui/src/components/tracker/TrackerCard.tsx @@ -4,6 +4,7 @@ import { DeviceDataT, TrackerDataT, TrackerStatus as TrackerStatusEnum, + TrackingChecklistStepT, } from 'solarxr-protocol'; import { Typography } from '@/components/commons/Typography'; import { TrackerBattery } from './TrackerBattery'; @@ -12,61 +13,10 @@ import { TrackerStatus } from './TrackerStatus'; import classNames from 'classnames'; import { useTracker } from '@/hooks/tracker'; import { BodyPartIcon } from '@/components/commons/BodyPartIcon'; -import { DownloadIcon } from '@/components/commons/icon/DownloadIcon'; -import { Link } from 'react-router-dom'; -import { useAppContext } from '@/hooks/app'; import { Tooltip } from '@/components/commons/Tooltip'; -import { Localized } from '@fluent/react'; -import { checkForUpdate } from '@/hooks/firmware-update'; - -function UpdateIcon({ - showUpdate, -}: { - showUpdate: - | 'can-update' - | 'low-battery' - | 'updated' - | 'unavailable' - | 'blocked'; -}) { - const content = ( -
-
-
- -
-
- ); - - return showUpdate !== 'can-update' ? ( - - - - } - > -
{content}
-
- ) : ( - - {content} - - ); -} +import { FirmwareIcon } from '@/components/commons/FirmwareIcon'; +import { WarningIcon } from '@/components/commons/icon/WarningIcon'; +import { trackingchecklistIdtoLabel } from '@/hooks/tracking-checklist'; function TrackerBig({ device, @@ -125,21 +75,39 @@ function TrackerBig({ function TrackerSmol({ device, tracker, + warning, }: { tracker: TrackerDataT; device?: DeviceDataT; + warning?: TrackingChecklistStepT | boolean; }) { const { useName } = useTracker(tracker); const trackerName = useName(); return ( -
-
- +
+
+ {warning && ( +
+ +
+ )} +
+ +
-
- + +
+ {trackerName} @@ -191,19 +159,12 @@ export function TrackerCard({ bg?: string; shakeHighlight?: boolean; onClick?: MouseEventHandler; - warning?: boolean; + warning?: TrackingChecklistStepT | boolean; showUpdates?: boolean; }) { - const { currentFirmwareRelease } = useAppContext(); const { useVelocity } = useTracker(tracker); const velocity = useVelocity(); - const showUpdate = - showUpdates && - tracker.status !== TrackerStatusEnum.DISCONNECTED && - currentFirmwareRelease && - device && - checkForUpdate(currentFirmwareRelease, device); return (
- {smol && } + {smol && ( + + + +
+ ) + } + > + + + )} {!smol && }
- {showUpdate && - showUpdate !== 'unavailable' && - showUpdate !== 'updated' && } + {showUpdates && }
); } diff --git a/gui/src/components/tracker/TrackerWifi.tsx b/gui/src/components/tracker/TrackerWifi.tsx index 10cde1bcd..319833a19 100644 --- a/gui/src/components/tracker/TrackerWifi.tsx +++ b/gui/src/components/tracker/TrackerWifi.tsx @@ -6,7 +6,7 @@ export function TrackerWifi({ ping, rssiShowNumeric, disabled, - textColor = 'secondary', + textColor = 'primary', }: { rssi: number | null; ping: number | null; diff --git a/gui/src/components/tracker/TrackersTable.tsx b/gui/src/components/tracker/TrackersTable.tsx index f9a4fcc8b..371f42add 100644 --- a/gui/src/components/tracker/TrackersTable.tsx +++ b/gui/src/components/tracker/TrackersTable.tsx @@ -1,13 +1,6 @@ -import { useLocalization } from '@fluent/react'; import classNames from 'classnames'; -import { IPv4 } from 'ip-num'; -import { MouseEventHandler, ReactNode, useMemo, useState } from 'react'; -import { - BodyPart, - TrackerDataT, - TrackerIdT, - TrackerStatus as TrackerStatusEnum, -} from 'solarxr-protocol'; +import { IPv4 } from 'ip-num/IPNumber'; +import { createContext, ReactNode, useContext, useMemo } from 'react'; import { useConfig } from '@/hooks/config'; import { useTracker } from '@/hooks/tracker'; import { BodyPartIcon } from '@/components/commons/BodyPartIcon'; @@ -16,37 +9,22 @@ import { formatVector3 } from '@/utils/formatting'; import { TrackerBattery } from './TrackerBattery'; import { TrackerStatus } from './TrackerStatus'; import { TrackerWifi } from './TrackerWifi'; -import { trackerStatusRelated, useStatusContext } from '@/hooks/status-system'; import { FlatDeviceTracker } from '@/store/app-store'; import { StayAlignedInfo } from '@/components/stay-aligned/StayAlignedInfo'; - -enum DisplayColumn { - NAME, - TYPE, - BATTERY, - PING, - TPS, - ROTATION, - TEMPERATURE, - LINEAR_ACCELERATION, - POSITION, - STAY_ALIGNED, - URL, -} - -const displayColumns: { [k: string]: boolean } = { - [DisplayColumn.NAME]: true, - [DisplayColumn.TYPE]: true, - [DisplayColumn.BATTERY]: true, - [DisplayColumn.PING]: true, - [DisplayColumn.TPS]: true, - [DisplayColumn.ROTATION]: true, - [DisplayColumn.TEMPERATURE]: true, - [DisplayColumn.LINEAR_ACCELERATION]: true, - [DisplayColumn.POSITION]: true, - [DisplayColumn.STAY_ALIGNED]: true, - [DisplayColumn.URL]: true, -}; +import { Tooltip } from '@/components/commons/Tooltip'; +import { WarningIcon } from '@/components/commons/icon/WarningIcon'; +import { FirmwareIcon } from '@/components/commons/FirmwareIcon'; +import { + BodyPart, + TrackerDataT, + TrackerStatus as TrackerStatusEnum, + TrackingChecklistStepT, +} from 'solarxr-protocol'; +import { + highlightedTrackers, + trackingchecklistIdtoLabel, + useTrackingChecklist, +} from '@/hooks/tracking-checklist'; const isHMD = ({ tracker }: FlatDeviceTracker) => tracker.info?.isHmd || tracker.info?.bodyPart === BodyPart.HEAD; @@ -58,15 +36,36 @@ const isSlime = ({ device }: FlatDeviceTracker) => const getTrackerName = ({ tracker }: FlatDeviceTracker) => tracker?.info?.customName?.toString() || ''; -export function TrackerNameCell({ tracker }: { tracker: TrackerDataT }) { +export function TrackerNameCell({ + tracker, + warning, +}: { + tracker: TrackerDataT; + warning: TrackingChecklistStepT | boolean; +}) { const { useName } = useTracker(tracker); const name = useName(); return ( -
-
- +
+
+ {warning && ( +
+ +
+ )} +
+ +
@@ -103,60 +102,195 @@ export function TrackerRotCell({ ); } -export function RowContainer({ +function Header({ + name, + className, + first = false, + last = false, + show = true, +}: { + first?: boolean; + last?: boolean; + name: string; + className?: string; + show?: boolean; +}) { + return ( + +
+ +
+ + ); +} + +function Cell({ children, - rounded = 'none', - hover, - tracker, - onClick, - onMouseOver, - onMouseOut, - warning, + first = false, + last = false, + show = true, }: { children: ReactNode; - rounded?: 'left' | 'right' | 'none'; - hover: boolean; - tracker: TrackerDataT; - onClick?: MouseEventHandler; - onMouseOver?: MouseEventHandler; - onMouseOut?: MouseEventHandler; - warning: boolean; + first?: boolean; + last?: boolean; + show?: boolean; }) { + const { tracker } = useContext(TrackerRowProvider); const { useVelocity } = useTracker(tracker); const velocity = useVelocity(); return ( -
+
{children}
-
+ + ); +} + +const TrackerRowProvider = createContext(undefined as never); + +function Row({ + data, + highlightedTrackers, + clickedTracker, +}: { + data: FlatDeviceTracker; + highlightedTrackers: highlightedTrackers | undefined; + clickedTracker: (tracker: TrackerDataT) => void; +}) { + const { config } = useConfig(); + const fontColor = config?.devSettings?.highContrast ? 'primary' : 'secondary'; + const moreInfo = config?.devSettings?.moreInfo; + + const { tracker, device } = data; + + const warning = + !!highlightedTrackers?.trackers.find( + (t) => + t?.deviceId?.id === tracker.trackerId?.deviceId?.id && + t?.trackerNum === tracker.trackerId?.trackerNum + ) && highlightedTrackers.step; + + return ( + + + + +
+ ) + } + tag="tr" + spacing={-5} + > + <> +
+
+ +
+
+ clickedTracker(tracker)}> + + + + + + {device?.hardwareInfo?.manufacturer || '--'} + + + + {device?.hardwareStatus?.batteryPctEstimate != null && ( + + )} + + + {(device?.hardwareStatus?.rssi != null || + device?.hardwareStatus?.ping != null) && ( + + )} + + + {tracker.tps && ( + {tracker.tps} + )} + + + + + + {tracker?.temp && tracker?.temp?.temp != 0 && ( + + {tracker.temp.temp.toFixed(2)} + + )} + + + {tracker.linearAcceleration && ( + + {formatVector3(tracker.linearAcceleration, 1)} + + )} + + + {tracker.position && ( + + {formatVector3(tracker.position, 2)} + + )} + + + + + + + udp:// + {IPv4.fromNumber( + device?.hardwareInfo?.ipAddress?.addr || 0 + ).toString()} + + + + + + ); } @@ -167,14 +301,8 @@ export function TrackersTable({ clickedTracker: (tracker: TrackerDataT) => void; flatTrackers: FlatDeviceTracker[]; }) { - const { l10n } = useLocalization(); - const [hoverTracker, setHoverTracker] = useState(null); const { config } = useConfig(); - const { statuses } = useStatusContext(); - - const trackerEqual = (id: TrackerIdT | null) => - id?.trackerNum == hoverTracker?.trackerNum && - (!id?.deviceId || id.deviceId.id == hoverTracker?.deviceId?.id); + const { highlightedTrackers } = useTrackingChecklist(); const filteringEnabled = config?.debug && config?.devSettings?.filterSlimesAndHMD; @@ -191,201 +319,51 @@ export function TrackersTable({ return list; }, [flatTrackers, filteringEnabled, sortingEnabled]); - const fontColor = config?.devSettings?.highContrast ? 'primary' : 'secondary'; const moreInfo = config?.devSettings?.moreInfo; - const hasTemperature = !!filteredSortedTrackers.find( - ({ tracker }) => tracker?.temp && tracker?.temp?.temp != 0 - ); - displayColumns[DisplayColumn.TEMPERATURE] = hasTemperature || false; - displayColumns[DisplayColumn.POSITION] = moreInfo || false; - displayColumns[DisplayColumn.LINEAR_ACCELERATION] = moreInfo || false; - displayColumns[DisplayColumn.STAY_ALIGNED] = moreInfo || false; - displayColumns[DisplayColumn.URL] = moreInfo || false; - const displayColumnsKeys = Object.keys(displayColumns).filter( - (k) => displayColumns[k] - ); - const firstColumnId = +displayColumnsKeys[0]; - const lastColumnId = +displayColumnsKeys[displayColumnsKeys.length - 1]; - - function column({ - id, - label, - labelClassName, - row, - }: { - id: DisplayColumn; - label: string; - labelClassName?: string; - row: (data: FlatDeviceTracker) => ReactNode | null; - }) { - let rounded: 'left' | 'right' | 'none' = 'none'; - if (firstColumnId === id) rounded = 'left'; - else if (lastColumnId === id) rounded = 'right'; - - if (!displayColumns[id]) return <>; - - return ( -
-
- {label} -
- {filteredSortedTrackers.map((data, index) => ( - clickedTracker(data.tracker)} - hover={trackerEqual(data.tracker.trackerId)} - onMouseOver={() => setHoverTracker(data.tracker.trackerId)} - onMouseOut={() => setHoverTracker(null)} - warning={Object.values(statuses).some((status) => - trackerStatusRelated(data.tracker, status) - )} - > - {row(data) || <>} - - ))} -
- ); - } - return ( -
- {column({ - id: DisplayColumn.NAME, - label: l10n.getString('tracker-table-column-name'), - row: ({ tracker }) => , - })} - - {column({ - id: DisplayColumn.TYPE, - label: l10n.getString('tracker-table-column-type'), - row: ({ device }) => ( - - {device?.hardwareInfo?.manufacturer || '--'} - - ), - })} - - {column({ - id: DisplayColumn.BATTERY, - label: l10n.getString('tracker-table-column-battery'), - row: ({ device, tracker }) => - device?.hardwareStatus?.batteryPctEstimate != null && ( - - ), - })} - - {column({ - id: DisplayColumn.PING, - label: l10n.getString('tracker-table-column-ping'), - row: ({ device, tracker }) => - (device?.hardwareStatus?.rssi != null || - device?.hardwareStatus?.ping != null) && ( - - ), - })} - - {column({ - id: DisplayColumn.TPS, - label: l10n.getString('tracker-table-column-tps'), - row: ({ tracker }) => ( - - {tracker?.tps != null ? <>{tracker.tps} : <>} - - ), - })} - - {column({ - id: DisplayColumn.ROTATION, - label: l10n.getString('tracker-table-column-rotation'), - labelClassName: classNames({ - 'w-44': config?.devSettings?.preciseRotation, - 'w-32': !config?.devSettings?.preciseRotation, - }), - row: ({ tracker }) => ( - + + +
+
+
+
+
+
- ), - })} - - {column({ - id: DisplayColumn.TEMPERATURE, - label: l10n.getString('tracker-table-column-temperature'), - row: ({ tracker }) => - tracker?.temp && - tracker?.temp?.temp != 0 && ( - - {`${tracker.temp.temp.toFixed(2)}`} - - ), - })} - - {column({ - id: DisplayColumn.LINEAR_ACCELERATION, - label: l10n.getString('tracker-table-column-linear-acceleration'), - labelClassName: 'w-36', - row: ({ tracker }) => - tracker.linearAcceleration && ( - - {formatVector3(tracker.linearAcceleration, 1)} - - ), - })} - - {column({ - id: DisplayColumn.POSITION, - label: l10n.getString('tracker-table-column-position'), - labelClassName: 'w-36', - row: ({ tracker }) => - tracker.position && ( - - {formatVector3(tracker.position, 2)} - - ), - })} - - {column({ - id: DisplayColumn.STAY_ALIGNED, - label: l10n.getString('tracker-table-column-stay_aligned'), - labelClassName: 'w-36', - row: ({ tracker }) => ( - - ), - })} - - {column({ - id: DisplayColumn.URL, - label: l10n.getString('tracker-table-column-url'), - row: ({ device }) => ( - - udp:// - {IPv4.fromNumber( - device?.hardwareInfo?.ipAddress?.addr || 0 - ).toString()} - - ), - })} +
+
+
+
+
+
+ {filteredSortedTrackers.map((data) => ( + + ))} +
); } diff --git a/gui/src/components/tracking-checklist/TrackingChecklist.tsx b/gui/src/components/tracking-checklist/TrackingChecklist.tsx new file mode 100644 index 000000000..1e3271e82 --- /dev/null +++ b/gui/src/components/tracking-checklist/TrackingChecklist.tsx @@ -0,0 +1,547 @@ +import { + TrackingChecklistStep, + TrackingChecklistContext, + useTrackingChecklist, + trackingchecklistIdtoLabel, +} from '@/hooks/tracking-checklist'; +import classNames from 'classnames'; +import { + ResetType, + TrackingChecklistPublicNetworksT, + TrackingChecklistStepId, +} from 'solarxr-protocol'; +import { ReactNode, useEffect, useMemo, useState } from 'react'; +import { openUrl } from '@tauri-apps/plugin-opener'; +import { CheckIcon } from '@/components/commons/icon/CheckIcon'; +import { Typography } from '@/components/commons/Typography'; +import { Button } from '@/components/commons/Button'; +import { ResetButton } from '@/components/home/ResetButton'; +import { A } from '@/components/commons/A'; +import { LoaderIcon, SlimeState } from '@/components/commons/icon/LoaderIcon'; +import { ProgressBar } from '@/components/commons/ProgressBar'; +import { CrossIcon } from '@/components/commons/icon/CrossIcon'; +import { + ArrowDownIcon, + ArrowRightIcon, +} from '@/components/commons/icon/ArrowIcons'; +import { Localized } from '@fluent/react'; +import { WrenchIcon } from '@/components/commons/icon/WrenchIcons'; +import { TrackingChecklistModal } from './TrackingChecklistModal'; +import { NavLink, useNavigate } from 'react-router-dom'; +import { useBreakpoint } from '@/hooks/breakpoint'; + +function Step({ + step: { status, id, optional, firstRequired }, + children, +}: { + step: TrackingChecklistStep; + index: number; + children: ReactNode; +}) { + const [open, setOpen] = useState(firstRequired); + + const canBeOpened = + (status === 'skipped' || status === 'invalid') && !firstRequired; + + useEffect(() => { + if (!canBeOpened) setOpen(false); + }, [open]); + + return ( +
+
{ + if (canBeOpened) setOpen((open) => !open); + }} + > +
+ {status === 'skipped' && } + {status === 'complete' && } + {(status === 'invalid' || status === 'blocked') && ( +
+ )} +
+
+ + {canBeOpened && ( +
+ +
+ )} +
+
+ {(firstRequired || open) && children && ( +
{children}
+ )} +
+ ); +} + +const stepContentLookup: Record< + number, + ( + step: TrackingChecklistStep, + context: TrackingChecklistContext + ) => JSX.Element +> = { + [TrackingChecklistStepId.TRACKERS_REST_CALIBRATION]: (step, { toggle }) => { + return ( +
+ +
+ {step.ignorable && ( +
+
+ ); + }, + [TrackingChecklistStepId.FULL_RESET]: () => { + return ( +
+ +
+ + + +
+
+
+ + Reset position +
+
+ + Reset position side +
+
+ + Reset position wrong +
+
+
+ +
+
+ ); + }, + [TrackingChecklistStepId.STEAMVR_DISCONNECTED]: (step, { toggle }) => { + return ( + <> +
+ +
+
+
+ + ); + }, + [TrackingChecklistStepId.TRACKER_ERROR]: () => { + return ; + }, + [TrackingChecklistStepId.UNASSIGNED_HMD]: () => { + return ; + }, + [TrackingChecklistStepId.NETWORK_PROFILE_PUBLIC]: (step, { toggle }) => { + const data = step.extraData as TrackingChecklistPublicNetworksT | null; + return ( + <> +
+ + ), + }} + whitespace="whitespace-pre-wrap" + /> +
+
+
+ + ); + }, + [TrackingChecklistStepId.VRCHAT_SETTINGS]: (step, { toggle }) => { + return ( + <> +
+ +
+
+
+ + ); + }, + [TrackingChecklistStepId.MOUNTING_CALIBRATION]: (step, { toggle }) => { + return ( +
+ + +
+ mounting reset ski pose +
+
+ + {step.ignorable && ( +
+
+ ); + }, + [TrackingChecklistStepId.FEET_MOUNTING_CALIBRATION]: (step, { toggle }) => { + return ( +
+ + +
+
+ mounting reset ski pose +
+
+ mounting reset ski pose +
+
+
+ + {step.ignorable && ( +
+
+ ); + }, + [TrackingChecklistStepId.STAY_ALIGNED_CONFIGURED]: (step, { toggle }) => { + return ( + <> +
+ +
+
+
+ + ); + }, +}; + +export function TrackingChecklistMobile() { + const context = useTrackingChecklist(); + const { completion, firstRequired, warnings } = context; + + return ( +
+ +
+ {completion === 'incomplete' ? 'Required:' : 'Warning:'}{' '} + +
+ +
+
+ ); +} + +export function TrackingChecklist({ + closable = true, + closed, + closing, + toggleClosed, +}: { + closable?: boolean; + closed: boolean; + closing: boolean; + toggleClosed: () => void; +}) { + const context = useTrackingChecklist(); + const { visibleSteps, progress, completion, warnings } = context; + + const slimeState = useMemo(() => { + if (completion === 'complete') return SlimeState.HAPPY; + if (completion === 'incomplete') return SlimeState.CURIOUS; + if (completion === 'partial') return SlimeState.SAD; + return SlimeState.HAPPY; + }, [completion]); + + const settingsOpenState = useState(false); + const [, setSettingsOpen] = settingsOpenState; + + return ( + <> +
+
+
+ +
+
+
setSettingsOpen(true)} + > + +
+ {closable && ( +
toggleClosed()} + > + {closed && } + {!closed && } +
+ )} +
+
+
+ {visibleSteps.map((step, index) => ( + + {stepContentLookup[step.id]?.(step, context) || undefined} + + ))} +
+
+
toggleClosed()} + > +
+
+
+
+ {completion === 'incomplete' && ( + + )} + {completion === 'partial' && ( + + )} + {completion == 'complete' && ( + + )} +
+
+
+
+ {!closed && ( + + )} + +
+
+ +
+
+
+
+ + + ); +} + +export function ChecklistPage() { + const nav = useNavigate(); + const { isMobile } = useBreakpoint('mobile'); + + useEffect(() => { + if (!isMobile) nav('/'); + }, [isMobile]); + + return ( +
+ {}} + /> +
+ ); +} diff --git a/gui/src/components/tracking-checklist/TrackingChecklistModal.tsx b/gui/src/components/tracking-checklist/TrackingChecklistModal.tsx new file mode 100644 index 000000000..687491ee8 --- /dev/null +++ b/gui/src/components/tracking-checklist/TrackingChecklistModal.tsx @@ -0,0 +1,36 @@ +import { Dispatch, SetStateAction } from 'react'; +import { BaseModal } from '@/components/commons/BaseModal'; +import { Typography } from '@/components/commons/Typography'; +import { Button } from '@/components/commons/Button'; +import { TrackingChecklistSettings } from '@/components/settings/pages/HomeScreenSettings'; + +export function TrackingChecklistModal({ + open, +}: { + open: [boolean, Dispatch>]; +}) { + return ( + { + open[1](false); + }} + > +
+ + +
+
+
+
+ ); +} diff --git a/gui/src/components/tracking-checklist/TrackingChecklistProvider.tsx b/gui/src/components/tracking-checklist/TrackingChecklistProvider.tsx new file mode 100644 index 000000000..0f65226dc --- /dev/null +++ b/gui/src/components/tracking-checklist/TrackingChecklistProvider.tsx @@ -0,0 +1,19 @@ +import { + TrackingChecklistContectC, + provideTrackingChecklist, +} from '@/hooks/tracking-checklist'; +import { ReactNode } from 'react'; + +export function TrackingChecklistProvider({ + children, +}: { + children: ReactNode; +}) { + const context = provideTrackingChecklist(); + + return ( + + {children} + + ); +} diff --git a/gui/src/components/vr-mode/VRModePage.tsx b/gui/src/components/vr-mode/VRModePage.tsx index d2e42dc6f..e651fab51 100644 --- a/gui/src/components/vr-mode/VRModePage.tsx +++ b/gui/src/components/vr-mode/VRModePage.tsx @@ -1,7 +1,10 @@ import { useEffect } from 'react'; import { useBreakpoint } from '@/hooks/breakpoint'; -import { WidgetsComponent } from '@/components/WidgetsComponent'; -import { useNavigate } from 'react-router-dom'; +import { NavLink, useNavigate } from 'react-router-dom'; +import { SkeletonVisualizerWidget } from '@/components/widgets/SkeletonVisualizerWidget'; +import { Checklist } from '@/components/commons/icon/ChecklistIcon'; +import { PreviewControls } from '@/components/Sidebar'; +import { Vector3 } from 'three'; export function VRModePage() { const nav = useNavigate(); @@ -12,8 +15,30 @@ export function VRModePage() { }, [isMobile]); return ( -
- +
+ { + context.addView({ + left: 0, + bottom: 0, + width: 1, + height: 1, + position: new Vector3(3, 2.5, -3), + onHeightChange(v, newHeight) { + v.controls.target.set(0, newHeight / 2.4, 0.1); + const scale = Math.max(1, newHeight) / 1; + v.camera.zoom = 1 / scale; + }, + }); + }} + /> + + + +
); } diff --git a/gui/src/components/vrc/VRCWarningsPage.tsx b/gui/src/components/vrc/VRCWarningsPage.tsx index 1b4df42fd..7de15bf97 100644 --- a/gui/src/components/vrc/VRCWarningsPage.tsx +++ b/gui/src/components/vrc/VRCWarningsPage.tsx @@ -100,7 +100,7 @@ const onOffKey = (value: boolean) => export function VRCWarningsPage() { const { l10n } = useLocalization(); - const { state, toggleMutedSettings, mutedSettings } = useVRCConfig(); + const { state, toggleMutedSettings } = useVRCConfig(); const { currentLocales } = useLocaleConfig(); const meterFormat = Intl.NumberFormat(currentLocales, { @@ -115,7 +115,7 @@ export function VRCWarningsPage() { const settingRowProps = (key: keyof VRCConfigStateSupported['validity']) => ({ mute: () => toggleMutedSettings(key), - muted: mutedSettings.includes(key), + muted: state.muted.includes(key), valid: state.validity[key] == true, }); diff --git a/gui/src/components/widgets/DeveloperModeWidget.tsx b/gui/src/components/widgets/DeveloperModeWidget.tsx index d26896cc4..fbe77bae5 100644 --- a/gui/src/components/widgets/DeveloperModeWidget.tsx +++ b/gui/src/components/widgets/DeveloperModeWidget.tsx @@ -4,7 +4,6 @@ import { useConfig } from '@/hooks/config'; import { useWebsocketAPI } from '@/hooks/websocket-api'; import { CheckBox } from '@/components/commons/Checkbox'; import { useLocalization } from '@fluent/react'; -import { Typography } from '@/components/commons/Typography'; export interface DeveloperModeWidgetForm { highContrast: boolean; @@ -57,6 +56,7 @@ export function DeveloperModeWidget() { key={index} control={control} variant="toggle" + outlined name={name} label={l10n.getString(`widget-developer_mode-${label}`)} /> @@ -73,10 +73,7 @@ export function DeveloperModeWidget() { }; return ( -
-
- {l10n.getString('widget-developer_mode')} -
+ {Object.entries(toggles).map(makeToggle)}
); diff --git a/gui/src/components/widgets/SkeletonVisualizerWidget.tsx b/gui/src/components/widgets/SkeletonVisualizerWidget.tsx index 078d743bf..2aa41503f 100644 --- a/gui/src/components/widgets/SkeletonVisualizerWidget.tsx +++ b/gui/src/components/widgets/SkeletonVisualizerWidget.tsx @@ -10,7 +10,6 @@ import * as THREE from 'three'; import { BodyPart, BoneT } from 'solarxr-protocol'; import { QuaternionFromQuatT, isIdentity } from '@/maths/quaternion'; import classNames from 'classnames'; -import { Button } from '@/components/commons/Button'; import { useLocalization } from '@fluent/react'; import { ErrorBoundary } from 'react-error-boundary'; import { Typography } from '@/components/commons/Typography'; @@ -18,8 +17,9 @@ import { useAtomValue } from 'jotai'; import { bonesAtom } from '@/store/app-store'; import { useConfig } from '@/hooks/config'; import { Tween } from '@tweenjs/tween.js'; +import { EyeIcon } from '@/components/commons/icon/EyeIcon'; -const GROUND_COLOR = '#4444aa'; +const GROUND_COLOR = '#2c2c6b'; // Just need to know the length of the total body, so don't need right legs const Y_PARTS = [ @@ -32,61 +32,6 @@ const Y_PARTS = [ BodyPart.LEFT_LOWER_LEG, ]; -interface SkeletonVisualizerWidgetProps { - height?: number | string; - maxHeight?: number | string; -} - -export function ToggleableSkeletonVisualizerWidget({ - height, - maxHeight, -}: SkeletonVisualizerWidgetProps) { - const { l10n } = useLocalization(); - const [enabled, setEnabled] = useState(false); - - useEffect(() => { - const state = localStorage.getItem('skeletonModelPreview'); - if (state) setEnabled(state === 'true'); - }, []); - - return ( - <> - {!enabled && ( - - )} - {enabled && ( -
- -
- -
-
- )} - - ); -} - export type SkeletonPreviewView = { left: number; bottom: number; @@ -110,7 +55,7 @@ function initializePreview( const resolution = new THREE.Vector2(canvas.clientWidth, canvas.clientHeight); const scene = new THREE.Scene(); - const renderer = new THREE.WebGLRenderer({ + let renderer: THREE.WebGLRenderer | null = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true, @@ -195,7 +140,7 @@ function initializePreview( const render = (delta: number) => { views.forEach((v) => { - if (v.hidden) return; + if (v.hidden || !renderer) return; v.controls.update(delta); const left = Math.floor(resolution.x * v.left); @@ -251,6 +196,7 @@ function initializePreview( resize: (width: number, height: number) => { resolution.set(width, height); skeletonHelper.resolution.copy(resolution); + if (!renderer) return; renderer.setSize(width, height); }, setFrameInterval: (interval: number) => { @@ -276,9 +222,11 @@ function initializePreview( } }, destroy: () => { - skeletonHelper.dispose(); - renderer.dispose(); cancelAnimationFrame(animationFrameId); + skeletonHelper.dispose(); + if (!renderer) return; + renderer.dispose(); + renderer = null; // Very important for js to free the WebGL context. dispose does not to it alone }, addView: ({ left, @@ -297,6 +245,8 @@ function initializePreview( hidden?: boolean; onHeightChange: (view: SkeletonPreviewView, newHeight: number) => void; }) => { + if (!renderer) return; + const camera = new THREE.PerspectiveCamera( 20, resolution.width / resolution.height, @@ -344,8 +294,10 @@ type PreviewContext = ReturnType; function SkeletonVisualizer({ onInit, + disabled = false, }: { onInit: (context: PreviewContext) => void; + disabled?: boolean; }) { const { config } = useConfig(); @@ -362,15 +314,15 @@ function SkeletonVisualizer({ useEffect(() => { if (bones.size === 0) return; const context = previewContext.current; - if (!context) return; + if (!context || disabled) return; context.rebuildSkeleton(createChildren(bones, BoneKind.root), bones); - }, [bones.size]); + }, [bones.size, disabled]); useEffect(() => { const context = previewContext.current; - if (!context) return; + if (!context || disabled) return; context.updatesBones(bones); - }, [bones]); + }, [bones, disabled]); const onResize = (e: ResizeObserverEntry) => { const context = previewContext.current; @@ -393,6 +345,7 @@ function SkeletonVisualizer({ }; useLayoutEffect(() => { + if (disabled) return; if (!canvasRef.current || !containerRef.current) throw 'invalid state - no canvas or container'; resizeObserver.current.observe(containerRef.current); @@ -416,11 +369,12 @@ function SkeletonVisualizer({ if (!previewContext.current || !containerRef.current) return; resizeObserver.current.unobserve(containerRef.current); previewContext.current.destroy(); + previewContext.current = null; containerRef.current.removeEventListener('mouseenter', onEnter); containerRef.current.removeEventListener('mouseleave', onLeave); }; - }, []); + }, [disabled]); return (
@@ -444,20 +398,53 @@ export function SkeletonVisualizerWidget({ }, }); }, + disabled = false, + toggleDisabled, }: { onInit?: (context: PreviewContext) => void; + disabled?: boolean; + toggleDisabled?: () => void; }) { const { l10n } = useLocalization(); + const [error, setError] = useState(false); return ( - - {l10n.getString('tips-failed_webgl')} - - } - > - - +
+
+ setError(true)} fallback={<>}> + + +
+
+
toggleDisabled?.()} + > + + +
+
+
+
+ + {l10n.getString('tips-failed_webgl')} + +
+
+
); } diff --git a/gui/src/hooks/app.ts b/gui/src/hooks/app.ts index 69fb07431..60a8fb8a0 100644 --- a/gui/src/hooks/app.ts +++ b/gui/src/hooks/app.ts @@ -3,16 +3,13 @@ import { DataFeedMessage, DataFeedUpdateT, ResetResponseT, - ResetStatus, - ResetType, RpcMessage, StartDataFeedT, } from 'solarxr-protocol'; -import { playSoundOnResetEnded, playSoundOnResetStarted } from '@/sounds/sounds'; +import { handleResetSounds } from '@/sounds/sounds'; import { useConfig } from './config'; import { useBonesDataFeedConfig, useDataFeedConfig } from './datafeed-config'; import { useWebsocketAPI } from './websocket-api'; -import { error } from '@/utils/logging'; import { useAtomValue, useSetAtom } from 'jotai'; import { bonesAtom, datafeedAtom, devicesAtom } from '@/store/app-store'; import { updateSentryContext } from '@/utils/sentry'; @@ -55,23 +52,9 @@ export function useProvideAppContext(): AppContext { updateSentryContext(devices); }, [devices]); - useRPCPacket(RpcMessage.ResetResponse, ({ status, resetType }: ResetResponseT) => { + useRPCPacket(RpcMessage.ResetResponse, (resetResponse: ResetResponseT) => { if (!config?.feedbackSound) return; - try { - switch (status) { - case ResetStatus.STARTED: { - if (resetType !== ResetType.Yaw) - playSoundOnResetStarted(config?.feedbackSoundVolume); - break; - } - case ResetStatus.FINISHED: { - playSoundOnResetEnded(resetType, config?.feedbackSoundVolume); - break; - } - } - } catch (e) { - error(e); - } + handleResetSounds(config?.feedbackSoundVolume ?? 1, resetResponse); }); useEffect(() => { diff --git a/gui/src/hooks/body-parts.ts b/gui/src/hooks/body-parts.ts new file mode 100644 index 000000000..a2ac2766f --- /dev/null +++ b/gui/src/hooks/body-parts.ts @@ -0,0 +1,90 @@ +import { BodyPart } from 'solarxr-protocol'; + +export const ALL_BODY_PARTS = [ + BodyPart.NONE, + BodyPart.HEAD, + BodyPart.NECK, + BodyPart.CHEST, + BodyPart.WAIST, + BodyPart.HIP, + BodyPart.LEFT_UPPER_LEG, + BodyPart.RIGHT_UPPER_LEG, + BodyPart.LEFT_LOWER_LEG, + BodyPart.RIGHT_LOWER_LEG, + BodyPart.LEFT_FOOT, + BodyPart.RIGHT_FOOT, + BodyPart.LEFT_LOWER_ARM, + BodyPart.RIGHT_LOWER_ARM, + BodyPart.LEFT_UPPER_ARM, + BodyPart.RIGHT_UPPER_ARM, + BodyPart.LEFT_HAND, + BodyPart.RIGHT_HAND, + BodyPart.LEFT_SHOULDER, + BodyPart.RIGHT_SHOULDER, + BodyPart.UPPER_CHEST, + BodyPart.LEFT_HIP, + BodyPart.RIGHT_HIP, + BodyPart.LEFT_THUMB_METACARPAL, + BodyPart.LEFT_THUMB_PROXIMAL, + BodyPart.LEFT_THUMB_DISTAL, + BodyPart.LEFT_INDEX_PROXIMAL, + BodyPart.LEFT_INDEX_INTERMEDIATE, + BodyPart.LEFT_INDEX_DISTAL, + BodyPart.LEFT_MIDDLE_PROXIMAL, + BodyPart.LEFT_MIDDLE_INTERMEDIATE, + BodyPart.LEFT_MIDDLE_DISTAL, + BodyPart.LEFT_RING_PROXIMAL, + BodyPart.LEFT_RING_INTERMEDIATE, + BodyPart.LEFT_RING_DISTAL, + BodyPart.LEFT_LITTLE_PROXIMAL, + BodyPart.LEFT_LITTLE_INTERMEDIATE, + BodyPart.LEFT_LITTLE_DISTAL, + BodyPart.RIGHT_THUMB_METACARPAL, + BodyPart.RIGHT_THUMB_PROXIMAL, + BodyPart.RIGHT_THUMB_DISTAL, + BodyPart.RIGHT_INDEX_PROXIMAL, + BodyPart.RIGHT_INDEX_INTERMEDIATE, + BodyPart.RIGHT_INDEX_DISTAL, + BodyPart.RIGHT_MIDDLE_PROXIMAL, + BodyPart.RIGHT_MIDDLE_INTERMEDIATE, + BodyPart.RIGHT_MIDDLE_DISTAL, + BodyPart.RIGHT_RING_PROXIMAL, + BodyPart.RIGHT_RING_INTERMEDIATE, + BodyPart.RIGHT_RING_DISTAL, + BodyPart.RIGHT_LITTLE_PROXIMAL, + BodyPart.RIGHT_LITTLE_INTERMEDIATE, + BodyPart.RIGHT_LITTLE_DISTAL, +]; +export const FEET_BODY_PARTS = [BodyPart.LEFT_FOOT, BodyPart.RIGHT_FOOT]; +export const FINGER_BODY_PARTS = [ + BodyPart.LEFT_THUMB_METACARPAL, + BodyPart.LEFT_THUMB_PROXIMAL, + BodyPart.LEFT_THUMB_DISTAL, + BodyPart.LEFT_INDEX_PROXIMAL, + BodyPart.LEFT_INDEX_INTERMEDIATE, + BodyPart.LEFT_INDEX_DISTAL, + BodyPart.LEFT_MIDDLE_PROXIMAL, + BodyPart.LEFT_MIDDLE_INTERMEDIATE, + BodyPart.LEFT_MIDDLE_DISTAL, + BodyPart.LEFT_RING_PROXIMAL, + BodyPart.LEFT_RING_INTERMEDIATE, + BodyPart.LEFT_RING_DISTAL, + BodyPart.LEFT_LITTLE_PROXIMAL, + BodyPart.LEFT_LITTLE_INTERMEDIATE, + BodyPart.LEFT_LITTLE_DISTAL, + BodyPart.RIGHT_THUMB_METACARPAL, + BodyPart.RIGHT_THUMB_PROXIMAL, + BodyPart.RIGHT_THUMB_DISTAL, + BodyPart.RIGHT_INDEX_PROXIMAL, + BodyPart.RIGHT_INDEX_INTERMEDIATE, + BodyPart.RIGHT_INDEX_DISTAL, + BodyPart.RIGHT_MIDDLE_PROXIMAL, + BodyPart.RIGHT_MIDDLE_INTERMEDIATE, + BodyPart.RIGHT_MIDDLE_DISTAL, + BodyPart.RIGHT_RING_PROXIMAL, + BodyPart.RIGHT_RING_INTERMEDIATE, + BodyPart.RIGHT_RING_DISTAL, + BodyPart.RIGHT_LITTLE_PROXIMAL, + BodyPart.RIGHT_LITTLE_INTERMEDIATE, + BodyPart.RIGHT_LITTLE_DISTAL, +]; diff --git a/gui/src/hooks/bvh.ts b/gui/src/hooks/bvh.ts new file mode 100644 index 000000000..e9202d432 --- /dev/null +++ b/gui/src/hooks/bvh.ts @@ -0,0 +1,54 @@ +import { useLocalization } from '@fluent/react'; +import { isTauri } from '@tauri-apps/api/core'; +import { useEffect, useState } from 'react'; +import { RecordBVHRequestT, RecordBVHStatusT, RpcMessage } from 'solarxr-protocol'; +import { useWebsocketAPI } from './websocket-api'; +import { useConfig } from './config'; +import { save } from '@tauri-apps/plugin-dialog'; + +export function useBHV() { + const { config } = useConfig(); + const { useRPCPacket, sendRPCPacket } = useWebsocketAPI(); + const [state, setState] = useState<'idle' | 'recording' | 'saving'>('idle'); + const { l10n } = useLocalization(); + + useEffect(() => { + sendRPCPacket(RpcMessage.RecordBVHStatusRequest, new RecordBVHRequestT()); + }, []); + + const toggle = async () => { + const record = new RecordBVHRequestT(state === 'recording'); + + if (isTauri() && state === 'idle') { + if (config?.bvhDirectory) { + record.path = config.bvhDirectory; + } else { + setState('saving'); + record.path = await save({ + title: l10n.getString('bvh-save_title'), + filters: [ + { + name: 'BVH', + extensions: ['bvh'], + }, + ], + defaultPath: 'bvh-recording.bvh', + }); + setState('idle'); + } + } + + sendRPCPacket(RpcMessage.RecordBVHRequest, record); + }; + + useRPCPacket(RpcMessage.RecordBVHStatus, (data: RecordBVHStatusT) => { + setState(data.recording ? 'recording' : 'idle'); + }); + + return { + available: + typeof window.__ANDROID__ === 'undefined' || !window.__ANDROID__?.isThere(), + state, + toggle, + }; +} diff --git a/gui/src/hooks/config.ts b/gui/src/hooks/config.ts index 1e69b343d..934afd7b0 100644 --- a/gui/src/hooks/config.ts +++ b/gui/src/hooks/config.ts @@ -46,6 +46,8 @@ export interface Config { showNavbarOnboarding: boolean; vrcMutedWarnings: string[]; bvhDirectory: string | null; + homeLayout: 'default' | 'table'; + skeletonPreview: boolean; } export interface ConfigContext { @@ -75,6 +77,8 @@ export const defaultConfig: Config = { vrcMutedWarnings: [], devSettings: defaultDevSettings, bvhDirectory: null, + homeLayout: 'default', + skeletonPreview: true, }; interface CrossStorage { diff --git a/gui/src/hooks/countdown.ts b/gui/src/hooks/countdown.ts index 38905033f..e07463c6a 100644 --- a/gui/src/hooks/countdown.ts +++ b/gui/src/hooks/countdown.ts @@ -17,13 +17,16 @@ export function useCountdown({ const startCountdown = () => { setIsCounting(true); setDisplayTimer(duration); + if (countdownTimer.current) { + clearTimer(); + } + counter.current = 0; countdownTimer.current = setInterval( () => { counter.current++; setDisplayTimer(duration - counter.current); if (counter.current >= duration) { - clearInterval(countdownTimer.current); resetEnd(); } }, @@ -31,15 +34,20 @@ export function useCountdown({ ); }; + const clearTimer = () => { + clearInterval(countdownTimer.current); + countdownTimer.current = undefined; + }; + const resetEnd = () => { setIsCounting(false); - clearInterval(countdownTimer.current); + clearTimer(); onCountdownEnd(); }; const abortCountdown = () => { setIsCounting(false); - clearInterval(countdownTimer.current); + clearTimer(); }; return { diff --git a/gui/src/hooks/pause-tracking.ts b/gui/src/hooks/pause-tracking.ts new file mode 100644 index 000000000..2760b14b8 --- /dev/null +++ b/gui/src/hooks/pause-tracking.ts @@ -0,0 +1,47 @@ +import { useEffect, useState } from 'react'; +import { useWebsocketAPI } from './websocket-api'; +import { + RpcMessage, + SetPauseTrackingRequestT, + TrackingPauseStateRequestT, + TrackingPauseStateResponseT, +} from 'solarxr-protocol'; +import { restartAndPlay, trackingPauseSound, trackingPlaySound } from '@/sounds/sounds'; +import { useConfig } from './config'; + +export function usePauseTracking() { + const { config } = useConfig(); + const { useRPCPacket, sendRPCPacket } = useWebsocketAPI(); + const [paused, setPaused] = useState(false); + + const toggle = () => { + const pause = new SetPauseTrackingRequestT(!paused); + sendRPCPacket(RpcMessage.SetPauseTrackingRequest, pause); + + if (!config) return; + if (pause.pauseTracking) { + restartAndPlay(trackingPauseSound, config.feedbackSoundVolume); + } else { + restartAndPlay(trackingPlaySound, config.feedbackSoundVolume); + } + }; + + useRPCPacket( + RpcMessage.TrackingPauseStateResponse, + (data: TrackingPauseStateResponseT) => { + setPaused(data.trackingPaused); + } + ); + + useEffect(() => { + sendRPCPacket( + RpcMessage.TrackingPauseStateRequest, + new TrackingPauseStateRequestT() + ); + }, []); + + return { + toggle, + paused, + }; +} diff --git a/gui/src/hooks/reset-settings.ts b/gui/src/hooks/reset-settings.ts new file mode 100644 index 000000000..8b94e20fa --- /dev/null +++ b/gui/src/hooks/reset-settings.ts @@ -0,0 +1,58 @@ +import { + ChangeSettingsRequestT, + ResetsSettingsT, + RpcMessage, + SettingsResetRequestT, + SettingsResponseT, +} from 'solarxr-protocol'; +import { useWebsocketAPI } from './websocket-api'; +import { useEffect, useState } from 'react'; + +export interface ResetSettingsForm { + resetMountingFeet: boolean; + armsMountingResetMode: number; + yawResetSmoothTime: number; + saveMountingReset: boolean; + resetHmdPitch: boolean; +} + +export const defaultResetSettings = { + resetMountingFeet: false, + armsMountingResetMode: 0, + yawResetSmoothTime: 0.0, + saveMountingReset: false, + resetHmdPitch: false, +}; + +export function loadResetSettings(resetSettingsForm: ResetSettingsForm) { + const resetsSettings = new ResetsSettingsT(); + resetsSettings.resetMountingFeet = resetSettingsForm.resetMountingFeet; + resetsSettings.armsMountingResetMode = resetSettingsForm.armsMountingResetMode; + resetsSettings.yawResetSmoothTime = resetSettingsForm.yawResetSmoothTime; + resetsSettings.saveMountingReset = resetSettingsForm.saveMountingReset; + resetsSettings.resetHmdPitch = resetSettingsForm.resetHmdPitch; + + return resetsSettings; +} + +export function useResetSettings() { + const { sendRPCPacket, useRPCPacket } = useWebsocketAPI(); + const [settings, setSettings] = useState(defaultResetSettings); + + useEffect(() => + sendRPCPacket(RpcMessage.SettingsRequest, new SettingsResetRequestT()) + ); + + useRPCPacket(RpcMessage.SettingsResponse, (settings: SettingsResponseT) => { + if (settings.resetsSettings) setSettings(settings.resetsSettings); + }); + + return { + update: (resetSettingsForm: Partial) => { + const req = new ChangeSettingsRequestT(); + const res = loadResetSettings({ ...settings, ...resetSettingsForm }); + req.resetsSettings = res; + sendRPCPacket(RpcMessage.ChangeSettingsRequest, req); + }, + }; +} diff --git a/gui/src/hooks/reset.ts b/gui/src/hooks/reset.ts new file mode 100644 index 000000000..712b15387 --- /dev/null +++ b/gui/src/hooks/reset.ts @@ -0,0 +1,151 @@ +import { useEffect, useMemo, useRef, useState } from 'react'; +import { + BodyPart, + ResetRequestT, + ResetResponseT, + ResetStatus, + ResetType, + RpcMessage, +} from 'solarxr-protocol'; +import { useWebsocketAPI } from './websocket-api'; +import { useAtomValue } from 'jotai'; +import { assignedTrackersAtom } from '@/store/app-store'; +import { FEET_BODY_PARTS, FINGER_BODY_PARTS } from './body-parts'; +import { useLocaleConfig } from '@/i18n/config'; + +export type ResetBtnStatus = 'idle' | 'counting' | 'finished'; + +export type MountingResetGroup = 'default' | 'feet' | 'fingers'; +export type UseResetOptions = + | { type: ResetType.Full | ResetType.Yaw } + | { type: ResetType.Mounting; group: MountingResetGroup }; + +export const BODY_PARTS_GROUPS: Record = { + default: [], + feet: FEET_BODY_PARTS, + fingers: FINGER_BODY_PARTS, +}; + +export function useReset(options: UseResetOptions, onReseted?: () => void) { + if (options.type === ResetType.Mounting && !options.group) options.group = 'default'; + + const { currentLocales } = useLocaleConfig(); + const { sendRPCPacket, useRPCPacket } = useWebsocketAPI(); + + const finishedTimeoutRef = useRef(); + const [status, setStatus] = useState('idle'); + const [progress, setProgress] = useState(0); + const [duration, setDuration] = useState(0); + + const parts = BODY_PARTS_GROUPS['group' in options ? options.group : 'default']; + + const triggerReset = () => { + const req = new ResetRequestT(); + req.resetType = options.type; + req.bodyParts = parts; + sendRPCPacket(RpcMessage.ResetRequest, req); + }; + + const onResetFinished = () => { + setStatus('finished'); + if (onReseted) onReseted(); + }; + + const onResetCanceled = () => { + if (status !== 'finished') setStatus('idle'); + }; + + useEffect(() => { + if (status === 'finished') { + finishedTimeoutRef.current = setTimeout(() => { + setStatus('idle'); // only do that if we were on finished status. Allows to reset the outlined border + }, 2000); + } else { + clearTimeout(finishedTimeoutRef.current); + } + return () => { + clearTimeout(finishedTimeoutRef.current); + }; + }, [status]); + + const onResetProgress = (progress: number, duration: number) => { + setProgress(progress / 1000); + setDuration(duration / 1000); + }; + + useRPCPacket( + RpcMessage.ResetResponse, + ({ status, resetType, progress, duration, bodyParts }: ResetResponseT) => { + if ( + resetType !== options.type || + (resetType == ResetType.Mounting && + JSON.stringify(parts) !== JSON.stringify(bodyParts)) + ) { + onResetCanceled(); + return; + } + onResetProgress(progress, duration); + switch (status) { + case ResetStatus.FINISHED: { + onResetFinished(); + break; + } + case ResetStatus.STARTED: { + setStatus('counting'); + break; + } + } + } + ); + + const name = useMemo(() => { + switch (options.type) { + case ResetType.Yaw: + return 'reset-yaw'; + case ResetType.Full: + return 'reset-full'; + case ResetType.Mounting: + if (options.group !== 'default') return `reset-mounting-${options.group}`; + return 'reset-mounting'; + default: + return 'unhandled'; + } + }, [options.type]); + + let disabled = status === 'counting'; + if (options.type === ResetType.Mounting && options.group !== 'default') { + const assignedTrackers = useAtomValue(assignedTrackersAtom); + + if ( + !assignedTrackers.some( + ({ tracker }) => + tracker.info?.bodyPart && + BODY_PARTS_GROUPS[options.group].includes(tracker.info?.bodyPart) + ) + ) + disabled = true; + } + + const localized = useMemo( + () => + Intl.NumberFormat('en-US', { + maximumFractionDigits: 1, + unit: 'second', + unitDisplay: 'narrow', + style: 'unit', + }), + [currentLocales] + ); + + return { + triggerReset, + progress, + duration, + status, + disabled, + name, + timer: localized.format(duration - progress), + }; +} + +export function useMountingReset() {} diff --git a/gui/src/hooks/status-system.ts b/gui/src/hooks/status-system.ts deleted file mode 100644 index 38226301e..000000000 --- a/gui/src/hooks/status-system.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { createContext, useEffect, useReducer, useContext } from 'react'; -import { - BodyPart, - RpcMessage, - StatusData, - StatusMessageT, - StatusPublicNetworkT, - StatusSteamVRDisconnectedT, - StatusSystemFixedT, - StatusSystemRequestT, - StatusSystemResponseT, - StatusSystemUpdateT, - StatusTrackerErrorT, - StatusTrackerResetT, - StatusUnassignedHMDT, - TrackerDataT, -} from 'solarxr-protocol'; -import { useWebsocketAPI } from './websocket-api'; -import { FluentVariable } from '@fluent/bundle'; -import { ReactLocalization } from '@fluent/react'; -import { FlatDeviceTracker } from '@/store/app-store'; - -type StatusSystemStateAction = - | StatusSystemStateFixedAction - | StatusSystemStateNewAction - | StatusSystemStateUpdateAction; - -interface StatusSystemStateFixedAction { - type: RpcMessage.StatusSystemFixed; - data: number; -} - -interface StatusSystemStateUpdateAction { - type: RpcMessage.StatusSystemUpdate; - data: StatusMessageT; -} - -interface StatusSystemStateNewAction { - type: RpcMessage.StatusSystemResponse; - data: StatusMessageT[]; -} - -interface StatusSystemState { - statuses: { - [id: number]: StatusMessageT; - }; -} - -export interface StatusSystemContext { - statuses: { - [id: number]: StatusMessageT; - }; -} - -function reducer( - state: StatusSystemState, - action: StatusSystemStateAction -): StatusSystemState { - switch (action.type) { - case RpcMessage.StatusSystemFixed: { - const newState = { - statuses: { ...state.statuses }, - }; - delete newState.statuses[action.data]; - return newState; - } - case RpcMessage.StatusSystemUpdate: { - return { - statuses: { ...state.statuses, [action.data.id]: action.data }, - }; - } - case RpcMessage.StatusSystemResponse: { - return { - // Convert the array into an object, we dont want to have an array on our map! - statuses: action.data.reduce((prev, cur) => ({ ...prev, [cur.id]: cur }), {}), - }; - } - } -} - -export function useProvideStatusContext(): StatusSystemContext { - const { useRPCPacket, sendRPCPacket, isConnected } = useWebsocketAPI(); - const [state, dispatch] = useReducer(reducer, { statuses: {} }); - - useRPCPacket( - RpcMessage.StatusSystemResponse, - ({ currentStatuses }: StatusSystemResponseT) => - dispatch({ type: RpcMessage.StatusSystemResponse, data: currentStatuses }) - ); - - useRPCPacket(RpcMessage.StatusSystemFixed, ({ fixedStatusId }: StatusSystemFixedT) => - dispatch({ type: RpcMessage.StatusSystemFixed, data: fixedStatusId }) - ); - - useRPCPacket( - RpcMessage.StatusSystemUpdate, - ({ newStatus }: StatusSystemUpdateT) => - newStatus && dispatch({ type: RpcMessage.StatusSystemUpdate, data: newStatus }) - ); - - useEffect(() => { - if (!isConnected) return; - sendRPCPacket(RpcMessage.StatusSystemRequest, new StatusSystemRequestT()); - }, [isConnected]); - - return state; -} - -export const StatusSystemC = createContext(undefined as never); - -export function useStatusContext() { - const context = useContext(StatusSystemC); - if (!context) { - throw new Error('useStatusContext must be within a StatusSystemContext Provider'); - } - return context; -} - -export function parseStatusToLocale( - status: StatusMessageT, - trackers: FlatDeviceTracker[] | null, - l10n: ReactLocalization -): Record { - switch (status.dataType) { - case StatusData.NONE: - case StatusData.StatusTrackerReset: - case StatusData.StatusUnassignedHMD: - return {}; - case StatusData.StatusPublicNetwork: { - const data = status.data as StatusPublicNetworkT; - return { - adapters: data.adapters.join(', '), - count: data.adapters.length, - }; - } - case StatusData.StatusSteamVRDisconnected: { - const data = status.data as StatusSteamVRDisconnectedT; - if (typeof data.bridgeSettingsName === 'string') { - return { type: data.bridgeSettingsName }; - } - return {}; - } - case StatusData.StatusTrackerError: { - const data = status.data as StatusTrackerErrorT; - if (data.trackerId?.trackerNum === undefined || !trackers) { - return {}; - } - - const tracker = trackers.find( - ({ tracker }) => - tracker?.trackerId?.trackerNum == data.trackerId?.trackerNum && - tracker?.trackerId?.deviceId?.id == data.trackerId?.deviceId?.id - ); - if (!tracker) - return { - trackerName: 'unknown', - }; - const name = tracker.tracker.info?.customName - ? tracker.tracker.info?.customName - : tracker.tracker.info?.bodyPart - ? l10n.getString('body_part-' + BodyPart[tracker.tracker.info?.bodyPart]) - : tracker.tracker.info?.displayName || 'unknown'; - if (typeof name !== 'string') { - return { - trackerName: new TextDecoder().decode(name), - }; - } - return { trackerName: name }; - } - } -} - -export const doesntContainTrackerInfo: readonly StatusData[] = [StatusData.NONE]; -export function trackerStatusRelated( - tracker: TrackerDataT, - status: StatusMessageT -): boolean { - if (doesntContainTrackerInfo.includes(status.dataType)) { - return false; - } - - switch (status.dataType) { - case StatusData.StatusTrackerReset: { - const data = status.data as StatusTrackerResetT; - return ( - data.trackerId?.trackerNum == tracker.trackerId?.trackerNum && - data.trackerId?.deviceId?.id === tracker.trackerId?.deviceId?.id - ); - } - case StatusData.StatusTrackerError: { - const data = status.data as StatusTrackerErrorT; - return ( - data.trackerId?.trackerNum == tracker.trackerId?.trackerNum && - data.trackerId?.deviceId?.id === tracker.trackerId?.deviceId?.id - ); - } - case StatusData.StatusUnassignedHMD: { - const data = status.data as StatusUnassignedHMDT; - return ( - data.trackerId?.trackerNum == tracker.trackerId?.trackerNum && - data.trackerId?.deviceId?.id === tracker.trackerId?.deviceId?.id - ); - } - default: - return false; - } -} diff --git a/gui/src/hooks/tracking-checklist.ts b/gui/src/hooks/tracking-checklist.ts new file mode 100644 index 000000000..b63cae9c9 --- /dev/null +++ b/gui/src/hooks/tracking-checklist.ts @@ -0,0 +1,193 @@ +import { + TrackingChecklistRequestT, + TrackingChecklistResponseT, + TrackingChecklistStepId, + TrackingChecklistStepT, + TrackingChecklistStepVisibility, + IgnoreTrackingChecklistStepRequestT, + RpcMessage, + TrackerIdT, +} from 'solarxr-protocol'; +import { useWebsocketAPI } from './websocket-api'; +import { createContext, useContext, useEffect, useMemo, useState } from 'react'; + +export const trackingchecklistIdtoLabel: Record = { + [TrackingChecklistStepId.UNKNOWN]: '', + [TrackingChecklistStepId.TRACKERS_REST_CALIBRATION]: + 'tracking_checklist-TRACKERS_REST_CALIBRATION', + [TrackingChecklistStepId.FULL_RESET]: 'tracking_checklist-FULL_RESET', + [TrackingChecklistStepId.VRCHAT_SETTINGS]: 'tracking_checklist-VRCHAT_SETTINGS', + [TrackingChecklistStepId.STEAMVR_DISCONNECTED]: + 'tracking_checklist-STEAMVR_DISCONNECTED', + [TrackingChecklistStepId.UNASSIGNED_HMD]: 'tracking_checklist-UNASSIGNED_HMD', + [TrackingChecklistStepId.TRACKER_ERROR]: 'tracking_checklist-TRACKER_ERROR', + [TrackingChecklistStepId.NETWORK_PROFILE_PUBLIC]: + 'tracking_checklist-NETWORK_PROFILE_PUBLIC', + [TrackingChecklistStepId.MOUNTING_CALIBRATION]: + 'tracking_checklist-MOUNTING_CALIBRATION', + [TrackingChecklistStepId.FEET_MOUNTING_CALIBRATION]: + 'tracking_checklist-FEET_MOUNTING_CALIBRATION', + [TrackingChecklistStepId.STAY_ALIGNED_CONFIGURED]: + 'tracking_checklist-STAY_ALIGNED_CONFIGURED', +}; + +export type TrackingChecklistStepStatus = + | 'complete' + | 'skipped' + | 'blocked' + | 'invalid'; +export type TrackingChecklistStep = TrackingChecklistStepT & { + status: TrackingChecklistStepStatus; + firstRequired: boolean; +}; +export type highlightedTrackers = { + step: TrackingChecklistStep; + trackers: Array; +}; + +const stepVisibility = ({ visibility, status, firstRequired }: TrackingChecklistStep) => + firstRequired || + visibility === TrackingChecklistStepVisibility.ALWAYS || + (visibility === TrackingChecklistStepVisibility.WHEN_INVALID && status != 'complete'); + +const createStep = ( + steps: TrackingChecklistStepT[], + step: TrackingChecklistStepT, + index: number +): TrackingChecklistStep => { + const previousSteps = steps.slice(0, index); + const previousBlocked = previousSteps.some( + ({ valid, optional }) => !valid && !optional + ); + + let status: TrackingChecklistStepStatus = 'complete'; + if (previousBlocked && !step.valid) status = 'blocked'; + if (!previousBlocked && !step.valid) status = 'invalid'; + + const firstRequiredIndex = steps.findIndex( + (s, index) => !s.valid || (index === steps.length - 1 && !s.valid) + ); + + const skipped = + steps.find( + (s, sIndex) => + (sIndex > index && s.valid && !s.optional) || sIndex === steps.length - 1 + ) || index === steps.length - 1; + if (!step.valid && step.optional && skipped) status = 'skipped'; + + return { + ...step, + status, + firstRequired: + firstRequiredIndex === index || + (firstRequiredIndex === -1 && index === steps.length - 1 && !step.valid), + pack: () => 0, + }; +}; + +export type TrackingChecklistContext = ReturnType; +export type Steps = { + steps: TrackingChecklistStepT[]; + visibleSteps: TrackingChecklistStep[]; + ignoredSteps: TrackingChecklistStepId[]; +}; +export function provideTrackingChecklist() { + const { sendRPCPacket, useRPCPacket } = useWebsocketAPI(); + const [steps, setSteps] = useState({ + steps: [], + visibleSteps: [], + ignoredSteps: [], + }); + + useRPCPacket( + RpcMessage.TrackingChecklistResponse, + (data: TrackingChecklistResponseT) => { + const activeSteps = data.steps.filter( + (step) => !data.ignoredSteps.includes(step.id) && step.enabled + ); + setSteps({ + steps: data.steps, + visibleSteps: activeSteps + .map((step: TrackingChecklistStepT, index) => + createStep(activeSteps, step, index) + ) + .filter(stepVisibility), + ignoredSteps: data.ignoredSteps, + }); + } + ); + + useEffect(() => { + sendRPCPacket(RpcMessage.TrackingChecklistRequest, new TrackingChecklistRequestT()); + }, []); + + const firstRequired = useMemo( + () => + steps.visibleSteps.find( + (step) => !step.valid && step.status != 'blocked' && !step.optional + ), + [steps] + ); + + const highlightedTrackers: highlightedTrackers | undefined = useMemo(() => { + if (!firstRequired || !firstRequired.extraData) return undefined; + if ('trackersId' in firstRequired.extraData) { + return { step: firstRequired, trackers: firstRequired.extraData.trackersId }; + } + if ('trackerId' in firstRequired.extraData && firstRequired.extraData.trackerId) { + return { step: firstRequired, trackers: [firstRequired.extraData.trackerId] }; + } + return { step: firstRequired, trackers: [] }; + }, [firstRequired]); + + const progress = useMemo(() => { + const completeSteps = steps.visibleSteps.filter( + (step) => step.status === 'complete' || step.status === 'skipped' + ); + return Math.min(1, completeSteps.length / steps.visibleSteps.length); + }, [steps]); + + const completion: 'complete' | 'partial' | 'incomplete' = useMemo(() => { + if (progress === 1 && steps.visibleSteps.find((step) => step.status === 'skipped')) + return 'partial'; + return progress === 1 || steps.visibleSteps.length === 0 + ? 'complete' + : 'incomplete'; + }, [progress, steps]); + + const warnings = useMemo( + () => steps.visibleSteps.filter((step) => !step.valid), + [steps] + ); + + const ignoreStep = (step: TrackingChecklistStepId, ignore: boolean) => { + const res = new IgnoreTrackingChecklistStepRequestT(); + res.stepId = step; + res.ignore = ignore; + sendRPCPacket(RpcMessage.IgnoreTrackingChecklistStepRequest, res); + }; + + return { + ...steps, + firstRequired, + highlightedTrackers, + progress, + completion, + warnings, + ignoreStep, + toggle: (step: TrackingChecklistStepId) => + ignoreStep(step, !steps.ignoredSteps.includes(step)), + }; +} + +export const TrackingChecklistContectC = createContext( + undefined as never +); + +export function useTrackingChecklist() { + const context = useContext(TrackingChecklistContectC); + if (!context) { + throw new Error('useTrackingChecklist must be within a TrackingChecklistProvider'); + } + return context; +} diff --git a/gui/src/hooks/vrc-config.ts b/gui/src/hooks/vrc-config.ts index 6251807b5..e38305df4 100644 --- a/gui/src/hooks/vrc-config.ts +++ b/gui/src/hooks/vrc-config.ts @@ -3,19 +3,19 @@ import { useWebsocketAPI } from './websocket-api'; import { RpcMessage, VRCAvatarMeasurementType, + VRCConfigSettingToggleMuteT, VRCConfigStateChangeResponseT, VRCConfigStateRequestT, VRCSpineMode, VRCTrackerModel, } from 'solarxr-protocol'; -import { useConfig } from './config'; type NonNull = { [P in keyof T]: NonNullable; }; export type VRCConfigStateSupported = { isSupported: true } & NonNull< - Pick + Pick >; export type VRCConfigState = { isSupported: false } | VRCConfigStateSupported; @@ -45,7 +45,6 @@ export const avatarMeasurementTypeTranslationMap: Record< export function useVRCConfig() { const { sendRPCPacket, useRPCPacket } = useWebsocketAPI(); - const { config, setConfig } = useConfig(); const [state, setState] = useState(null); useLayoutEffect(() => { @@ -59,31 +58,20 @@ export function useVRCConfig() { } ); - const mutedSettings = useMemo(() => { - if (!state?.isSupported) return []; - return Object.keys(state.validity).filter((k) => - config?.vrcMutedWarnings.includes(k) - ); - }, [state, config]); - const invalidConfig = useMemo(() => { if (!state?.isSupported) return false; return Object.entries(state.validity) - .filter(([k]) => !config?.vrcMutedWarnings.includes(k)) + .filter(([k]) => !state.muted.includes(k)) .some(([, v]) => !v); - }, [state, config]); + }, [state]); return { state, invalidConfig, - mutedSettings, toggleMutedSettings: async (key: keyof VRCConfigStateSupported['validity']) => { - if (!config) return; - const index = config.vrcMutedWarnings.findIndex((v) => v === key); - if (index === -1) config.vrcMutedWarnings.push(key); - else config?.vrcMutedWarnings.splice(index, 1); - await setConfig(config); - console.log(config.vrcMutedWarnings); + const req = new VRCConfigSettingToggleMuteT(); + req.key = key; + sendRPCPacket(RpcMessage.VRCConfigSettingToggleMute, req); }, }; } diff --git a/gui/src/index.scss b/gui/src/index.scss index 1cadf718d..28c5673d6 100644 --- a/gui/src/index.scss +++ b/gui/src/index.scss @@ -63,12 +63,11 @@ body { background: theme('colors.background.20'); --navbar-w: 101px; - --widget-w: 274px; --topbar-h: 38px; @screen mobile { --topbar-h: 44px; - --navbar-h: 90px; + --navbar-h: 73px; } } @@ -91,7 +90,7 @@ body { --accent-background-50: 46, 33, 69; --success: 80, 232, 151; - --warning: 216, 205, 55; + --warning: 255, 225, 53; --critical: 223, 109, 140; --special: 164, 79, 237; --window-icon-stroke: 192, 161, 216; @@ -153,7 +152,7 @@ body { --accent-background-50: 19, 57, 19; --success: 80, 232, 151; - --warning: 216, 205, 55; + --warning: 255, 225, 53; --critical: 223, 109, 140; --special: 54, 161, 54; --window-icon-stroke: 129, 213, 130; @@ -179,7 +178,7 @@ body { --accent-background-50: 57, 57, 19; --success: 80, 232, 151; - --warning: 216, 205, 55; + --warning: 255, 225, 53; --critical: 223, 109, 140; --special: 161, 160, 54; --window-icon-stroke: 213, 212, 129; @@ -205,7 +204,7 @@ body { --accent-background-50: 57, 34, 19; --success: 80, 232, 151; - --warning: 216, 205, 55; + --warning: 255, 225, 53; --critical: 223, 109, 140; --special: 161, 95, 54; --window-icon-stroke: 213, 162, 129; @@ -231,7 +230,7 @@ body { --accent-background-50: 57, 19, 19; --success: 80, 232, 151; - --warning: 216, 205, 55; + --warning: 255, 225, 53; --critical: 223, 109, 140; --special: 161, 54, 54; --window-icon-stroke: 213, 129, 129; @@ -257,7 +256,7 @@ body { --accent-background-50: 39, 39, 39; --success: 80, 232, 151; - --warning: 216, 205, 55; + --warning: 255, 225, 53; --critical: 223, 109, 140; --special: 121, 121, 121; --window-icon-stroke: 172, 172, 172; diff --git a/gui/src/sounds/sounds.ts b/gui/src/sounds/sounds.ts index b890ebd41..2e55e5130 100644 --- a/gui/src/sounds/sounds.ts +++ b/gui/src/sounds/sounds.ts @@ -1,4 +1,4 @@ -import { ResetType } from 'solarxr-protocol'; +import { ResetResponseT, ResetStatus, ResetType } from 'solarxr-protocol'; import Xylophone, { ValidNote } from './xylophone'; const tones: ValidNote[][] = [ @@ -10,47 +10,46 @@ const tones: ValidNote[][] = [ ]; const xylophone = new Xylophone(); - -export async function playSoundOnResetEnded(resetType: ResetType, volume = 1) { - switch (resetType) { - case ResetType.Yaw: { - xylophone.play({ - notes: ['C4'], - offset: 0.15, - type: 'custom', - volume, - }); - break; - } - case ResetType.Full: { - xylophone.play({ - notes: ['E3', 'G3'], - offset: 0.15, - type: 'custom', - volume, - }); - break; - } - case ResetType.Mounting: { - xylophone.play({ - notes: ['G3', 'B3', 'D4'], - offset: 0.15, - type: 'custom', - volume, - }); - break; - } +const mew = createAudio('/sounds/mew.ogg'); +const resetSounds: Record< + ResetType, + { + initial: HTMLAudioElement | null; + tick: HTMLAudioElement[] | null; + end: HTMLAudioElement; + mew: HTMLAudioElement | null; } -} +> = { + [ResetType.Full]: { + initial: createAudio('/sounds/full-reset/init-full-reset-with-tail.ogg'), + tick: [ + createAudio('/sounds/full-reset/full-click-1.ogg'), + createAudio('/sounds/full-reset/full-click-2.ogg'), + createAudio('/sounds/full-reset/full-click-3.ogg'), + ], + end: createAudio('/sounds/full-reset/end-full-reset-with-tail.ogg'), + mew, + }, + [ResetType.Yaw]: { + initial: null, + tick: null, + end: createAudio('/sounds/yaw-reset/yaw-reset.ogg'), + mew: null, + }, + [ResetType.Mounting]: { + initial: createAudio('/sounds/mounting-reset/init-mounting-reset-with-tail.ogg'), + tick: [ + createAudio('/sounds/mounting-reset/mount-click-1.ogg'), + createAudio('/sounds/mounting-reset/mount-click-2.ogg'), + createAudio('/sounds/mounting-reset/mount-click-3.ogg'), + ], + end: createAudio('/sounds/mounting-reset/end-mounting-reset-with-tail.ogg'), + mew, + }, +}; -export async function playSoundOnResetStarted(volume = 1) { - await xylophone.play({ - notes: ['A4'], - offset: 0.4, - type: 'custom', - volume, - }); -} +export const trackingPauseSound = createAudio('/sounds/tracking/pause.ogg'); +export const trackingPlaySound = createAudio('/sounds/tracking/play.ogg'); let lastTap = 0; export async function playTapSetupSound(volume = 1) { @@ -84,3 +83,58 @@ export async function playTapSetupSound(volume = 1) { lastTap = 0; } } + +function createAudio(path: string): HTMLAudioElement { + const audio = new Audio(path); + audio.preload = 'auto'; + audio.load(); + return audio; +} + +export function restartAndPlay(audio: HTMLAudioElement | null, volume: number) { + if (!audio) return; + try { + audio.load(); // LINUX: Solves wierd bug where webkit would unload sounds wierdly and make the sounds not play anymore + + audio.volume = Math.min(1, Math.pow(volume, Math.E) + 0.05); + audio.currentTime = 0; + const playPromise = audio.play(); + if (playPromise !== undefined) { + playPromise.catch((error) => { + console.error('Audio playback failed:', error); + }); + } + } catch (error) { + console.error('Audio error:', error); + } +} + +export function handleResetSounds( + volume: number, + { progress, status, resetType }: ResetResponseT +) { + if (!resetSounds) throw 'sounds not loaded'; + const sounds = resetSounds[resetType]; + + if (status === ResetStatus.STARTED) { + if (progress === 0) { + performance.mark('sound_start'); + restartAndPlay(sounds.initial, volume); + } + + if (sounds.tick) { + const tickIndex = (progress / 1000) % sounds.tick.length; + if (progress >= 1000 && sounds.tick[tickIndex]) { + restartAndPlay(sounds.tick[tickIndex], volume); + } + } + } + + if (status === ResetStatus.FINISHED) { + performance.mark('sound_end'); + console.log(performance.measure('sound', 'sound_start', 'sound_end')); + + restartAndPlay(sounds.end, volume); + restartAndPlay(sounds.mew, volume); + } +} diff --git a/gui/src/store/app-store.ts b/gui/src/store/app-store.ts index 3b48ab44a..5786b8cc8 100644 --- a/gui/src/store/app-store.ts +++ b/gui/src/store/app-store.ts @@ -9,6 +9,7 @@ import { } from 'solarxr-protocol'; import { selectAtom } from 'jotai/utils'; import { isEqual } from '@react-hookz/deep-equal'; +import { FEET_BODY_PARTS, FINGER_BODY_PARTS } from '@/hooks/body-parts'; export interface FlatDeviceTracker { device?: DeviceDataT; @@ -45,14 +46,18 @@ export const unassignedTrackersAtom = atom((get) => { return trackers.filter(({ tracker }) => tracker.info?.bodyPart === BodyPart.NONE); }); -export const connectedIMUTrackersAtom = atom((get) => { +export const connectedTrackersAtom = atom((get) => { const trackers = get(flatTrackersAtom); return trackers.filter( - ({ tracker }) => - tracker.status !== TrackerStatus.DISCONNECTED && tracker.info?.isImu + ({ tracker }) => tracker.status !== TrackerStatus.DISCONNECTED ); }); +export const connectedIMUTrackersAtom = atom((get) => { + const trackers = get(connectedTrackersAtom); + return trackers.filter(({ tracker }) => tracker.info?.isImu); +}); + export const computedTrackersAtom = selectAtom( datafeedAtom, (datafeed) => datafeed.syntheticTrackers.map((tracker) => ({ tracker })), @@ -95,3 +100,16 @@ export const trackerFromIdAtom = ({ (a) => a, isEqual ); + +export const feetAssignedTrackers = atom((get) => + get(assignedTrackersAtom).some( + (t) => t.tracker.info?.bodyPart && FEET_BODY_PARTS.includes(t.tracker.info.bodyPart) + ) +); + +export const fingerAssignedTrackers = atom((get) => + get(assignedTrackersAtom).some( + (t) => + t.tracker.info?.bodyPart && FINGER_BODY_PARTS.includes(t.tracker.info.bodyPart) + ) +); diff --git a/gui/src/utils/skeletonHelper.ts b/gui/src/utils/skeletonHelper.ts index 0ff8f8e23..eb77d95a4 100644 --- a/gui/src/utils/skeletonHelper.ts +++ b/gui/src/utils/skeletonHelper.ts @@ -188,11 +188,11 @@ export class BoneKind extends Bone { case BodyPart.NONE: throw 'Unexpected body part'; case BodyPart.HEAD: - return new Color('black'); + return new Color('gold'); case BodyPart.NECK: return new Color('silver'); case BodyPart.UPPER_CHEST: - return new Color('blue'); + return new Color('chartreuse'); case BodyPart.CHEST: return new Color('purple'); case BodyPart.WAIST: @@ -201,13 +201,13 @@ export class BoneKind extends Bone { return new Color('orange'); case BodyPart.LEFT_UPPER_LEG: case BodyPart.RIGHT_UPPER_LEG: - return new Color('blue'); + return new Color('chartreuse'); case BodyPart.LEFT_LOWER_LEG: case BodyPart.RIGHT_LOWER_LEG: return new Color('teal'); case BodyPart.LEFT_FOOT: case BodyPart.RIGHT_FOOT: - return new Color('#00ffcc'); + return new Color('gold'); case BodyPart.LEFT_LOWER_ARM: case BodyPart.RIGHT_LOWER_ARM: return new Color('red'); diff --git a/gui/tailwind.config.ts b/gui/tailwind.config.ts index 34ab01a0b..cf61eee43 100644 --- a/gui/tailwind.config.ts +++ b/gui/tailwind.config.ts @@ -3,6 +3,7 @@ import forms from '@tailwindcss/forms'; import typography from '@tailwindcss/typography'; import gradient from 'tailwind-gradient-mask-image'; import type { Config } from 'tailwindcss'; +import { transform } from 'typescript'; const colors = { 'blue-gray': { @@ -172,6 +173,7 @@ const config = { nsm: { raw: 'not (min-width: 900px)' }, sm: '900px', md: '1100px', + nmd: { raw: 'not (min-width: 1100px)' }, 'md-max': { raw: 'not (min-width: 1100px)' }, lg: '1300px', xl: '1600px', @@ -228,6 +230,54 @@ const config = { 'animation-timing-function': 'cubic-bezier(0.8, 0, 1, 1)', }, }, + 'timer-tick': { + "0%, 40%": { + transform: 'scale(1)', + }, + "20%": { + transform: 'scale(1.3)', + }, + }, + 'spin-ccw': { + '0%': { + transform: 'rotate(0deg)', + }, + '100%': { + transform: 'rotate(-360deg)', + }, + }, + skiing: { + '0%, 100%': { + transform: 'rotate(0deg) translateX(0%) translateY(0%)', + }, + '10%': { + transform: 'rotate(12deg) translateX(-5%) translateY(5%)', + }, + '20%': { + transform: 'rotate(10deg) translateX(0%) translateY(0%)', + }, + '30%': { + transform: 'rotate(12deg) translateX(5%) translateY(-5%)', + }, + '40%': { + transform: 'rotate(10deg) translateX(0%) translateY(0%)', + }, + '50%': { + transform: 'rotate(12deg) translateX(-5%) translateY(5%)', + }, + '60%': { + transform: 'rotate(10deg) translateX(0%) translateY(0%)', + }, + '70%': { + transform: 'rotate(12deg) translateX(5%) translateY(-5%)', + }, + '80%': { + transform: 'rotate(10deg) translateX(0%) translateY(0%)', + }, + '90%': { + transform: 'rotate(10deg) translateX(-5%) translateY(5%)', + }, + }, }, backgroundImage: { slime: `linear-gradient(135deg, ${colors.purple[100]} 50%, ${colors['blue-gray'][700]} 50% 100%)`, @@ -240,6 +290,11 @@ const config = { 'trans-flag': `linear-gradient(135deg, ${colors['trans-blue'][800]} 40%, ${colors['trans-blue'][700]} 40% 70%, ${colors['trans-blue'][600]} 70% 100%)`, 'asexual-flag': `linear-gradient(135deg, ${colors['asexual'][100]} 30%, ${colors['asexual'][200]} 30% 50%, ${colors['asexual'][300]} 50% 70%, ${colors['asexual'][400]} 70% 100%)`, }, + animation: { + 'spin-ccw': 'spin-ccw 1s linear infinite', + 'timer-tick': 'timer-tick 1s linear infinite', + skiing: 'skiing 1s linear infinite', + }, }, data: { checked: 'checked=true', diff --git a/gui/tsconfig.json b/gui/tsconfig.json index 5de11b699..a89d9f33b 100644 --- a/gui/tsconfig.json +++ b/gui/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { - "target": "es2022", - "lib": ["dom", "dom.iterable", "ES2023"], + "target": "es2023", + "lib": ["dom", "dom.iterable", "es2023"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 571c03745..7a4cf380e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,10 +10,10 @@ importers: devDependencies: husky: specifier: ^9.1.6 - version: 9.1.7 + version: 9.1.6 lint-staged: specifier: ^15.2.10 - version: 15.5.2 + version: 15.2.10 gui: dependencies: @@ -25,61 +25,61 @@ importers: version: 0.15.2(@fluent/bundle@0.18.0)(react@18.3.1) '@fontsource/poppins': specifier: ^5.1.0 - version: 5.2.6 + version: 5.1.0 '@formatjs/intl-localematcher': specifier: ^0.2.32 version: 0.2.32 '@hookform/resolvers': specifier: ^3.6.0 - version: 3.10.0(react-hook-form@7.65.0(react@18.3.1)) + version: 3.6.0(react-hook-form@7.66.0(react@18.3.1)) '@react-hookz/deep-equal': specifier: ^3.0.3 - version: 3.0.4 + version: 3.0.3 '@react-three/drei': specifier: ^9.114.3 - version: 9.122.0(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(@types/react@18.3.24)(@types/three@0.163.0)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)(use-sync-external-store@1.5.0(react@18.3.1)) + version: 9.114.5(@react-three/fiber@8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(@types/react@18.3.11)(@types/three@0.163.0)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0) '@react-three/fiber': specifier: ^8.17.10 - version: 8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0) + version: 8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0) '@sentry/react': specifier: ^9.9.0 - version: 9.46.0(react@18.3.1) + version: 9.9.0(react@18.3.1) '@sentry/vite-plugin': specifier: ^2.22.7 - version: 2.23.1 + version: 2.22.7 '@tailwindcss/typography': specifier: ^0.5.15 - version: 0.5.16(tailwindcss@3.4.17(ts-node@9.1.1(typescript@5.9.2))) + version: 0.5.15(tailwindcss@3.4.14(ts-node@9.1.1(typescript@5.6.3))) '@tanstack/react-query': specifier: ^5.48.0 - version: 5.87.1(react@18.3.1) + version: 5.48.0(react@18.3.1) '@tauri-apps/api': - specifier: ~2 - version: 2.8.0 + specifier: ^2.0.2 + version: 2.0.2 '@tauri-apps/plugin-dialog': - specifier: ~2 - version: 2.4.0 + specifier: ^2.0.0 + version: 2.0.0 '@tauri-apps/plugin-fs': - specifier: ~2 - version: 2.4.2 + specifier: 2.4.1 + version: 2.4.1 '@tauri-apps/plugin-http': - specifier: ~2 - version: 2.5.2 + specifier: ^2.5.0 + version: 2.5.0 '@tauri-apps/plugin-log': specifier: ~2 - version: 2.7.0 + version: 2.7.1 '@tauri-apps/plugin-opener': - specifier: ~2 - version: 2.5.0 - '@tauri-apps/plugin-os': - specifier: ~2 - version: 2.3.1 - '@tauri-apps/plugin-shell': - specifier: ~2 - version: 2.3.1 - '@tauri-apps/plugin-store': - specifier: ~2 + specifier: ^2.4.0 version: 2.4.0 + '@tauri-apps/plugin-os': + specifier: ^2.0.0 + version: 2.0.0 + '@tauri-apps/plugin-shell': + specifier: ^2.3.0 + version: 2.3.0 + '@tauri-apps/plugin-store': + specifier: ^2.4.1 + version: 2.4.1 '@tweenjs/tween.js': specifier: ^25.0.0 version: 25.0.0 @@ -103,10 +103,10 @@ importers: version: 2.0.1 ip-num: specifier: ^1.5.1 - version: 1.5.2 + version: 1.5.1 jotai: specifier: ^2.12.2 - version: 2.13.1(@babel/core@7.28.4)(@babel/template@7.27.2)(@types/react@18.3.24)(react@18.3.1) + version: 2.12.2(@types/react@18.3.11)(react@18.3.1) prompts: specifier: ^2.4.2 version: 2.4.2 @@ -118,31 +118,31 @@ importers: version: 18.3.1(react@18.3.1) react-error-boundary: specifier: ^4.0.13 - version: 4.1.2(react@18.3.1) + version: 4.0.13(react@18.3.1) react-helmet: specifier: ^6.1.0 version: 6.1.0(react@18.3.1) react-hook-form: specifier: ^7.63.0 - version: 7.65.0(react@18.3.1) + version: 7.66.0(react@18.3.1) react-markdown: specifier: ^9.0.1 - version: 9.1.0(@types/react@18.3.24)(react@18.3.1) + version: 9.0.1(@types/react@18.3.11)(react@18.3.1) react-modal: specifier: ^3.16.1 - version: 3.16.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 3.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-responsive: specifier: ^10.0.0 - version: 10.0.1(react@18.3.1) + version: 10.0.0(react@18.3.1) react-router-dom: specifier: ^6.26.2 - version: 6.30.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) remark-gfm: specifier: ^4.0.0 - version: 4.0.1 + version: 4.0.0 semver: specifier: ^7.6.3 - version: 7.7.2 + version: 7.6.3 solarxr-protocol: specifier: file:../solarxr-protocol version: link:../solarxr-protocol @@ -151,47 +151,47 @@ importers: version: 0.163.0 ts-pattern: specifier: ^5.4.0 - version: 5.8.0 + version: 5.5.0 typescript: specifier: ^5.6.3 - version: 5.9.2 + version: 5.6.3 use-double-tap: specifier: ^1.3.6 - version: 1.3.7(react@18.3.1) + version: 1.3.6(react@18.3.1) yup: specifier: ^1.4.0 - version: 1.7.0 + version: 1.4.0 devDependencies: '@dword-design/eslint-plugin-import-alias': specifier: ^4.0.9 version: 4.0.9 '@openapi-codegen/cli': specifier: ^2.0.2 - version: 2.0.4(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 2.0.2(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@openapi-codegen/typescript': specifier: ^8.0.2 - version: 8.1.1 + version: 8.0.2 '@stylistic/eslint-plugin': specifier: ^5.5.0 - version: 5.5.0(eslint@9.39.0(jiti@1.21.6)) + version: 5.5.0(eslint@9.39.1(jiti@1.21.6)) '@tailwindcss/forms': specifier: ^0.5.9 - version: 0.5.10(tailwindcss@3.4.17(ts-node@9.1.1(typescript@5.9.2))) + version: 0.5.9(tailwindcss@3.4.14(ts-node@9.1.1(typescript@5.6.3))) '@tauri-apps/cli': specifier: ~2 - version: 2.8.4 + version: 2.0.3 '@types/file-saver': specifier: ^2.0.7 version: 2.0.7 '@types/node': specifier: ^24.3.1 - version: 24.3.1 + version: 24.10.0 '@types/react': specifier: ^18.3.11 - version: 18.3.24 + version: 18.3.11 '@types/react-dom': specifier: ^18.3.0 - version: 18.3.7(@types/react@18.3.24) + version: 18.3.0 '@types/react-helmet': specifier: ^6.1.11 version: 6.1.11 @@ -200,61 +200,61 @@ importers: version: 3.16.3 '@types/semver': specifier: ^7.5.8 - version: 7.7.1 + version: 7.5.8 '@types/three': specifier: ^0.163.0 version: 0.163.0 '@typescript-eslint/eslint-plugin': specifier: ^7.18.0 - version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2))(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3))(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) '@typescript-eslint/parser': specifier: ^7.18.0 - version: 7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) + version: 7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) '@vitejs/plugin-react': specifier: ^4.3.2 - version: 4.7.0(vite@5.4.19(@types/node@24.3.1)(sass@1.92.0)(terser@5.31.1)) + version: 4.3.2(vite@5.4.9(@types/node@24.10.0)(sass@1.80.2)(terser@5.31.1)) autoprefixer: specifier: ^10.4.20 - version: 10.4.21(postcss@8.5.6) + version: 10.4.20(postcss@8.4.38) cross-env: specifier: ^7.0.3 version: 7.0.3 dotenv: specifier: ^16.4.5 - version: 16.6.1 + version: 16.4.5 eslint: specifier: ^9.39.0 - version: 9.39.0(jiti@1.21.6) + version: 9.39.1(jiti@1.21.6) eslint-config-airbnb: specifier: ^19.0.4 - version: 19.0.4(eslint-plugin-import@2.32.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.0(jiti@1.21.6)))(eslint-plugin-react-hooks@4.6.2(eslint@9.39.0(jiti@1.21.6)))(eslint-plugin-react@7.37.5(eslint@9.39.0(jiti@1.21.6)))(eslint@9.39.0(jiti@1.21.6)) + version: 19.0.4(eslint-plugin-import@2.32.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@1.21.6)))(eslint-plugin-react-hooks@4.6.2(eslint@9.39.1(jiti@1.21.6)))(eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@1.21.6)))(eslint@9.39.1(jiti@1.21.6)) eslint-import-resolver-typescript: specifier: ^3.10.1 - version: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.0(jiti@1.21.6)) + version: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.6)) eslint-plugin-import: specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.0(jiti@1.21.6)) + version: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.6)) eslint-plugin-jsx-a11y: specifier: ^6.10.2 - version: 6.10.2(eslint@9.39.0(jiti@1.21.6)) + version: 6.10.2(eslint@9.39.1(jiti@1.21.6)) eslint-plugin-react: specifier: ^7.37.5 - version: 7.37.5(eslint@9.39.0(jiti@1.21.6)) + version: 7.37.5(eslint@9.39.1(jiti@1.21.6)) eslint-plugin-react-hooks: specifier: ^4.6.2 - version: 4.6.2(eslint@9.39.0(jiti@1.21.6)) + version: 4.6.2(eslint@9.39.1(jiti@1.21.6)) globals: specifier: ^15.10.0 - version: 15.15.0 + version: 15.10.0 prettier: specifier: ^3.3.3 - version: 3.6.2 + version: 3.3.3 rollup-plugin-visualizer: specifier: ^5.12.0 - version: 5.14.0(rollup@4.50.0) + version: 5.12.0(rollup@4.24.0) sass: specifier: ^1.79.4 - version: 1.92.0 + version: 1.80.2 spdx-satisfies: specifier: ^5.0.1 version: 5.0.1 @@ -263,13 +263,13 @@ importers: version: 1.2.0 tailwindcss: specifier: ^3.4.13 - version: 3.4.17(ts-node@9.1.1(typescript@5.9.2)) + version: 3.4.14(ts-node@9.1.1(typescript@5.6.3)) typescript-eslint: specifier: ^8.46.2 - version: 8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) + version: 8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) vite: specifier: ^5.4.8 - version: 5.4.19(@types/node@24.3.1)(sass@1.92.0)(terser@5.31.1) + version: 5.4.9(@types/node@24.10.0)(sass@1.80.2)(terser@5.31.1) solarxr-protocol: dependencies: @@ -286,21 +286,21 @@ importers: packages: - '@alcalzone/ansi-tokenize@0.1.3': - resolution: {integrity: sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==} - engines: {node: '>=14.13.1'} - '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@apollo/client@3.14.0': - resolution: {integrity: sha512-0YQKKRIxiMlIou+SekQqdCo0ZTHxOcES+K8vKB53cIDpwABNR0P0yRzPgsbgcj3zRJniD93S/ontsnZsCLZrxQ==} + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@apollo/client@3.10.6': + resolution: {integrity: sha512-3lLFGJtzC1/mEnK11BRf+Bf8536kBQUSB1G9yMtcRsxmY+tCKdTPzsP3fMUKy10BPIE0sDUY1pux3iMPIn2vow==} peerDependencies: graphql: ^15.0.0 || ^16.0.0 - graphql-ws: ^5.5.5 || ^6.0.3 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc + graphql-ws: ^5.5.5 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 subscriptions-transport-ws: ^0.9.0 || ^0.11.0 peerDependenciesMeta: graphql-ws: @@ -312,98 +312,189 @@ packages: subscriptions-transport-ws: optional: true - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.4': - resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + '@babel/code-frame@7.25.7': + resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.4': - resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + '@babel/compat-data@7.24.7': + resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.3': - resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + '@babel/compat-data@7.25.8': + resolution: {integrity: sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.2': - resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + '@babel/core@7.24.7': + resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} engines: {node: '>=6.9.0'} - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + '@babel/core@7.25.8': + resolution: {integrity: sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.27.1': - resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + '@babel/generator@7.24.7': + resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.3': - resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + '@babel/generator@7.25.7': + resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.24.7': + resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.7': + resolution: {integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==} + engines: {node: '>=6.9.0'} + + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-function-name@7.24.7': + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-hoist-variables@7.24.7': + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.25.7': + resolution: {integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.24.7': + resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.27.1': - resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + '@babel/helper-module-transforms@7.25.7': + resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.24.7': + resolution: {integrity: sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + '@babel/helper-simple-access@7.25.7': + resolution: {integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.4': - resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + '@babel/helper-string-parser@7.24.7': + resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.4': - resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + '@babel/helper-string-parser@7.25.7': + resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.25.7': + resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.7': + resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.25.7': + resolution: {integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.24.7': + resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.25.7': + resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.25.7': + resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.24.7': + resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-transform-react-jsx-self@7.27.1': - resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + '@babel/parser@7.25.8': + resolution: {integrity: sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.24.7': + resolution: {integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-source@7.27.1': - resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + '@babel/plugin-transform-react-jsx-source@7.24.7': + resolution: {integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + '@babel/runtime@7.24.7': + resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} engines: {node: '>=6.9.0'} - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + '@babel/template@7.24.7': + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.4': - resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + '@babel/template@7.25.7': + resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.4': - resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + '@babel/traverse@7.24.7': + resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.25.7': + resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.24.7': + resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.25.8': + resolution: {integrity: sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==} engines: {node: '>=6.9.0'} '@dword-design/dedent@0.7.0': resolution: {integrity: sha512-OFmAmzKiDUh9m7WRMYcoEOPI7b5tS5hdqQmtKDwF+ZssVJv8a+GHo9VOtFsmlw3h8Roh/9QzFWIsjSFZyQUMdg==} - '@dword-design/endent@1.4.7': - resolution: {integrity: sha512-yK1ivALMq9Eys6O9SgO+jBpRbQBLruFL2FvWMWPcoqiY1hPDHleY02Vir6jN6j7tDiMog3tjVtqKb4eWXdKo1w==} + '@dword-design/endent@1.4.1': + resolution: {integrity: sha512-e2sCTzth5kyRdM0o+yEb5wBVzUdJL8Y6HblRGRV0Bif0knf1ZjRLhUjdCrqM+Muirb68X/xJzgdRDJVmLqgXGA==} '@dword-design/eslint-plugin-import-alias@4.0.9': resolution: {integrity: sha512-GbDZ0HJOnLgc8X2iGM+SW/2Y2Zi8qJ96+zsJWO4nhfEmHSKPcC3MUldVFLrrHy1z5R+MEba6tp7455U2UYB0bQ==} @@ -414,11 +505,11 @@ packages: engines: {node: '>=14'} deprecated: Use lodash and endent - '@emnapi/core@1.5.0': - resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} + '@emnapi/core@1.7.0': + resolution: {integrity: sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==} - '@emnapi/runtime@1.5.0': - resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + '@emnapi/runtime@1.7.0': + resolution: {integrity: sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==} '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} @@ -561,12 +652,22 @@ packages: cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.0': resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.10.1': + resolution: {integrity: sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint-community/regexpp@4.12.2': resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -587,8 +688,8 @@ packages: resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.0': - resolution: {integrity: sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==} + '@eslint/js@9.39.1': + resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -619,8 +720,8 @@ packages: peerDependencies: '@fluent/bundle': '>= 0.13.0' - '@fontsource/poppins@5.2.6': - resolution: {integrity: sha512-NFr0121+vWCfzLDVvSZOzwAjRUaNj6QHA9gtUh/KHHoWf1Qn23EkwVFs63XZ6UtF5K1b3UNmCx77YEMGgV7BOQ==} + '@fontsource/poppins@5.1.0': + resolution: {integrity: sha512-tpLXlnNi2fwQjiipvuj4uNFHCdoLA8izRsKdoexZuEzjx0r/g1aKLf4ta6lFgF7L+/+AFdmaXFlUwwvmDzYH+g==} '@formatjs/intl-localematcher@0.2.32': resolution: {integrity: sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==} @@ -630,8 +731,8 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@hookform/resolvers@3.10.0': - resolution: {integrity: sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==} + '@hookform/resolvers@3.6.0': + resolution: {integrity: sha512-UBcpyOX3+RR+dNnqBd0lchXpoL8p4xC21XP8H6Meb8uve5Br1GCnmg0PcBoKKqPKgGu9GHQ/oygcmPrQhetwqw==} peerDependencies: react-hook-form: ^7.0.0 @@ -655,37 +756,36 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/source-map@0.3.11': - resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} - '@jridgewell/trace-mapping@0.3.30': - resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@jridgewell/sourcemap-codec@1.4.15': + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@mediapipe/tasks-vision@0.10.17': - resolution: {integrity: sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==} + '@mediapipe/tasks-vision@0.10.8': + resolution: {integrity: sha512-Rp7ll8BHrKB3wXaRFKhrltwZl1CiXGdibPxuWXvqGnKTnv8fqa/nvftYNuSbf+pbJWKYCXdBtYTITdAUTGGh0Q==} '@mgit-at/typescript-flatbuffers-codegen@0.1.3': resolution: {integrity: sha512-sf9vaoiR/SR0dpV568GhsoLbd6659StJ4Gl9jszZL/bsJJaF5VmLYbI57OSI4JDm+L6d3osVMl9mkchox9j6/g==} hasBin: true - '@monogrid/gainmap-js@3.1.0': - resolution: {integrity: sha512-Obb0/gEd/HReTlg8ttaYk+0m62gQJmCblMOjHSMHRrBP2zdfKMHLCRbh/6ex9fSUJMKdjjIEiohwkbGD3wj2Nw==} + '@monogrid/gainmap-js@3.0.5': + resolution: {integrity: sha512-53sCTG4FaJBaAq/tcufARtVYDMDGqyBT9i7F453pWGhZ5LqubDHDWtYoHo9VhQqMcHTEexdJqSsR58y+9HVmQA==} peerDependencies: three: '>= 0.159.0' @@ -708,12 +808,12 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} - '@openapi-codegen/cli@2.0.4': - resolution: {integrity: sha512-ekeOMArpFZBTTOHYkKb08wE7XXC46CCgFhTzrbQSYc7Xj23GIiR130pcCn2tipb5PZcPL4nlwkZdMJC+WhWGvw==} + '@openapi-codegen/cli@2.0.2': + resolution: {integrity: sha512-uBk6yOBSBIgGWA2ok/IjBS03UwVAIpnan0lKz2sk3tsSe8rVIjOnQPxGYvSuByfxzdIu+nrPom2meqtcjlMvDQ==} hasBin: true - '@openapi-codegen/typescript@8.1.1': - resolution: {integrity: sha512-KGnFyoQc4lhtVVvX6lk2btF9GDDUbu8Jvn5C89aHuyIA4nor/Rlt7f7QHaYpecFDX8RZrPcYGrDZ8OFNUkEOlA==} + '@openapi-codegen/typescript@8.0.2': + resolution: {integrity: sha512-7X9WR+qlIMcMxiBgheGzyQcChLSPVqNYf9SAFJdTOJQLWfy+gaXiDonUC8WC7p6Hpz7eM6OLU1i7f/h+2RlH1w==} '@parcel/watcher-android-arm64@2.4.1': resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==} @@ -795,58 +895,59 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@react-hookz/deep-equal@3.0.4': - resolution: {integrity: sha512-QpcSUTP1I0lI8/GvSEvclkoEGRWjbYxuUWaecxr8eJRceivGF18wHoJwaej2NYxMn7SqhKaZ9JYQvmdATiSG8A==} + '@react-hookz/deep-equal@3.0.3': + resolution: {integrity: sha512-SLy+NmiDpncqc2d9TR4Y4R7f8lUFOQK9WbnIq02A6wDxy+dTHfA2Np0dPvj0SFp6i1nqERLmEUe9MxPLuO/IqA==} engines: {node: '>=18.0.0'} + deprecated: Package is deprecated and will be deleted soon. Use @ver0/deep-equal instead. - '@react-spring/animated@9.7.5': - resolution: {integrity: sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==} + '@react-spring/animated@9.6.1': + resolution: {integrity: sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - '@react-spring/core@9.7.5': - resolution: {integrity: sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==} + '@react-spring/core@9.6.1': + resolution: {integrity: sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - '@react-spring/rafz@9.7.5': - resolution: {integrity: sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==} + '@react-spring/rafz@9.6.1': + resolution: {integrity: sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==} - '@react-spring/shared@9.7.5': - resolution: {integrity: sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==} + '@react-spring/shared@9.6.1': + resolution: {integrity: sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - '@react-spring/three@9.7.5': - resolution: {integrity: sha512-RxIsCoQfUqOS3POmhVHa1wdWS0wyHAUway73uRLp3GAL5U2iYVNdnzQsep6M2NZ994BlW8TcKuMtQHUqOsy6WA==} + '@react-spring/three@9.6.1': + resolution: {integrity: sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA==} peerDependencies: '@react-three/fiber': '>=6.0' react: ^16.8.0 || ^17.0.0 || ^18.0.0 three: '>=0.126' - '@react-spring/types@9.7.5': - resolution: {integrity: sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==} + '@react-spring/types@9.6.1': + resolution: {integrity: sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==} - '@react-three/drei@9.122.0': - resolution: {integrity: sha512-SEO/F/rBCTjlLez7WAlpys+iGe9hty4rNgjZvgkQeXFSiwqD4Hbk/wNHMAbdd8vprO2Aj81mihv4dF5bC7D0CA==} + '@react-three/drei@9.114.5': + resolution: {integrity: sha512-nXD/wOwQVaaKF1WXG5Ah3ief+Mojm5YInlk91tanzEYdG+5Vhno34AFn3xt0XKMAaHA+Lkjfi+BpqnVama+JPA==} peerDependencies: - '@react-three/fiber': ^8 - react: ^18 - react-dom: ^18 + '@react-three/fiber': '>=8.0' + react: '>=18.0' + react-dom: '>=18.0' three: '>=0.137' peerDependenciesMeta: react-dom: optional: true - '@react-three/fiber@8.18.0': - resolution: {integrity: sha512-FYZZqD0UUHUswKz3LQl2Z7H24AhD14XGTsIRw3SJaXUxyfVMi+1yiZGmqTcPt/CkPpdU7rrxqcyQ1zJE5DjvIQ==} + '@react-three/fiber@8.17.10': + resolution: {integrity: sha512-S6bqa4DqUooEkInYv/W+Jklv2zjSYCXAhm6qKpAQyOXhTEt5gBXnA7W6aoJ0bjmp9pAeaSj/AZUoz1HCSof/uA==} peerDependencies: expo: '>=43.0' expo-asset: '>=8.4' expo-file-system: '>=11.0' expo-gl: '>=11.0' - react: '>=18 <19' - react-dom: '>=18 <19' + react: '>=18.0' + react-dom: '>=18.0' react-native: '>=0.64' three: '>=0.133' peerDependenciesMeta: @@ -863,147 +964,119 @@ packages: react-native: optional: true - '@remix-run/router@1.23.0': - resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} + '@remix-run/router@1.20.0': + resolution: {integrity: sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==} engines: {node: '>=14.0.0'} - '@rolldown/pluginutils@1.0.0-beta.27': - resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} - - '@rollup/rollup-android-arm-eabi@4.50.0': - resolution: {integrity: sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==} + '@rollup/rollup-android-arm-eabi@4.24.0': + resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.50.0': - resolution: {integrity: sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==} + '@rollup/rollup-android-arm64@4.24.0': + resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.50.0': - resolution: {integrity: sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==} + '@rollup/rollup-darwin-arm64@4.24.0': + resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.50.0': - resolution: {integrity: sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==} + '@rollup/rollup-darwin-x64@4.24.0': + resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.50.0': - resolution: {integrity: sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.50.0': - resolution: {integrity: sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.50.0': - resolution: {integrity: sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==} + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': + resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.50.0': - resolution: {integrity: sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==} + '@rollup/rollup-linux-arm-musleabihf@4.24.0': + resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.50.0': - resolution: {integrity: sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==} + '@rollup/rollup-linux-arm64-gnu@4.24.0': + resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.50.0': - resolution: {integrity: sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==} + '@rollup/rollup-linux-arm64-musl@4.24.0': + resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.50.0': - resolution: {integrity: sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-ppc64-gnu@4.50.0': - resolution: {integrity: sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==} + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': + resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.50.0': - resolution: {integrity: sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==} + '@rollup/rollup-linux-riscv64-gnu@4.24.0': + resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.50.0': - resolution: {integrity: sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.50.0': - resolution: {integrity: sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==} + '@rollup/rollup-linux-s390x-gnu@4.24.0': + resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.50.0': - resolution: {integrity: sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==} + '@rollup/rollup-linux-x64-gnu@4.24.0': + resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.50.0': - resolution: {integrity: sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==} + '@rollup/rollup-linux-x64-musl@4.24.0': + resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.50.0': - resolution: {integrity: sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.50.0': - resolution: {integrity: sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==} + '@rollup/rollup-win32-arm64-msvc@4.24.0': + resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.50.0': - resolution: {integrity: sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==} + '@rollup/rollup-win32-ia32-msvc@4.24.0': + resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.50.0': - resolution: {integrity: sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==} + '@rollup/rollup-win32-x64-msvc@4.24.0': + resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==} cpu: [x64] os: [win32] '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@sentry-internal/browser-utils@9.46.0': - resolution: {integrity: sha512-Q0CeHym9wysku8mYkORXmhtlBE0IrafAI+NiPSqxOBKXGOCWKVCvowHuAF56GwPFic2rSrRnub5fWYv7T1jfEQ==} + '@sentry-internal/browser-utils@9.9.0': + resolution: {integrity: sha512-V/YhKLis98JFkqBGZaEBlDNFpJHJjoCvNb05raAYXdITfDIl37Kxqj0zX+IzyRhqnswkQ+DBTyoEoci09IR2bQ==} engines: {node: '>=18'} - '@sentry-internal/feedback@9.46.0': - resolution: {integrity: sha512-KLRy3OolDkGdPItQ3obtBU2RqDt9+KE8z7r7Gsu7c6A6A89m8ZVlrxee3hPQt6qp0YY0P8WazpedU3DYTtaT8w==} + '@sentry-internal/feedback@9.9.0': + resolution: {integrity: sha512-hrxuOLm0Xsnx75hTNt3eLgNNjER3egrHZShdRzlMiakfKpA9f2X10z75vlZmT5ZUygDQnp9UVUnu28cDuVb9Zw==} engines: {node: '>=18'} - '@sentry-internal/replay-canvas@9.46.0': - resolution: {integrity: sha512-QcBjrdRWFJrrrjbmrr2bbrp2R9RYj1KMEbhHNT2Lm1XplIQw+tULEKOHxNtkUFSLR1RNje7JQbxhzM1j95FxVQ==} + '@sentry-internal/replay-canvas@9.9.0': + resolution: {integrity: sha512-YK0ixGjquahGpNsQskCEVwycdHlwNBLCx9XJr1BmGnlOw6fUCmpyVetaGg/ZyhkzKGNXAGoTa4s7FUFnAG4bKg==} engines: {node: '>=18'} - '@sentry-internal/replay@9.46.0': - resolution: {integrity: sha512-+8JUblxSSnN0FXcmOewbN+wIc1dt6/zaSeAvt2xshrfrLooVullcGsuLAiPhY0d/e++Fk06q1SAl9g4V0V13gg==} + '@sentry-internal/replay@9.9.0': + resolution: {integrity: sha512-EWczKMu3qiZ0SUUWU3zkGod+AWD/VQCLiQw+tw+PEpdHbRZIdYKsEptengZCFKthrwe2QmYpVCTSRxGvujJ/6g==} engines: {node: '>=18'} - '@sentry/babel-plugin-component-annotate@2.23.1': - resolution: {integrity: sha512-l1z8AvI6k9I+2z49OgvP3SlzB1M0Lw24KtceiJibNaSyQwxsItoT9/XftZ/8BBtkosVmNOTQhL1eUsSkuSv1LA==} + '@sentry/babel-plugin-component-annotate@2.22.7': + resolution: {integrity: sha512-aa7XKgZMVl6l04NY+3X7BP7yvQ/s8scn8KzQfTLrGRarziTlMGrsCOBQtCNWXOPEbtxAIHpZ9dsrAn5EJSivOQ==} engines: {node: '>= 14'} - '@sentry/browser@9.46.0': - resolution: {integrity: sha512-NOnCTQCM0NFuwbyt4DYWDNO2zOTj1mCf43hJqGDFb1XM9F++7zAmSNnCx4UrEoBTiFOy40McJwBBk9D1blSktA==} + '@sentry/browser@9.9.0': + resolution: {integrity: sha512-pIMdkOC+iggZefBs6ck5fL1mBhbLzjdw/8K99iqSeDh+lLvmlHVZajAhPlmw50xfH8CyQ1s22dhcL+zXbg3NKw==} engines: {node: '>=18'} - '@sentry/bundler-plugin-core@2.23.1': - resolution: {integrity: sha512-JA6utNiwMKv6Jfj0Hmk0DI/XUizSHg7HhhkFETKhRlYEhZAdkyz1atDBg0ncKNgRBKyHeSYWcMFtUyo26VB76w==} + '@sentry/bundler-plugin-core@2.22.7': + resolution: {integrity: sha512-ouQh5sqcB8vsJ8yTTe0rf+iaUkwmeUlGNFi35IkCFUQlWJ22qS6OfvNjOqFI19e6eGUXks0c/2ieFC4+9wJ+1g==} engines: {node: '>= 14'} '@sentry/cli-darwin@2.39.1': @@ -1052,18 +1125,18 @@ packages: engines: {node: '>= 10'} hasBin: true - '@sentry/core@9.46.0': - resolution: {integrity: sha512-it7JMFqxVproAgEtbLgCVBYtQ9fIb+Bu0JD+cEplTN/Ukpe6GaolyYib5geZqslVxhp2sQgT+58aGvfd/k0N8Q==} + '@sentry/core@9.9.0': + resolution: {integrity: sha512-GxKvx8PSgoWhLLS+/WBGIXy7rsFcnJBPDqFXIfcAGy89k2j06d9IP0kiIc63qBGStSUkh5FFJLPTakZ5RXiFXA==} engines: {node: '>=18'} - '@sentry/react@9.46.0': - resolution: {integrity: sha512-2NTlke1rKAJO2JIY1RCrv8EjfXXkLc+AC61PpgF1QjH/cz0NuCZ6gpQi6M5qS7anAGPjaOE1t3QdLeOEI/Q3kA==} + '@sentry/react@9.9.0': + resolution: {integrity: sha512-7BE2Lx5CNtHtlNSS7Z9HxKquohC0xhdFceO3NlMXlx+dZuVCMoQmLISB8SQEcHw+2VO24MvtP3LPEzdeNbkIfg==} engines: {node: '>=18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x - '@sentry/vite-plugin@2.23.1': - resolution: {integrity: sha512-avtjtIQ019sZW3FklpmNNsQOnYZjCHpnVxgDGElfZb+AaR4AvtHNlxXLJp+iqEfSK+Xok8MJarJqIgCaWcF40Q==} + '@sentry/vite-plugin@2.22.7': + resolution: {integrity: sha512-sYRNiNm4toQGq2BfZSJPdw36em3eQaLu+3NTFpA7Hl4g3Sp2Rt3CYObnW5bxlFEruRhxzvdyB383N9OefVZ6KA==} engines: {node: '>= 14'} '@sindresorhus/is@5.6.0': @@ -1076,71 +1149,71 @@ packages: peerDependencies: eslint: '>=9.0.0' - '@swc/core-darwin-arm64@1.13.5': - resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==} + '@swc/core-darwin-arm64@1.6.5': + resolution: {integrity: sha512-RGQhMdni2v1/ANQ/2K+F+QYdzaucekYBewZcX1ogqJ8G5sbPaBdYdDN1qQ4kHLCIkPtGP6qC7c71qPEqL2RidQ==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.13.5': - resolution: {integrity: sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==} + '@swc/core-darwin-x64@1.6.5': + resolution: {integrity: sha512-/pSN0/Jtcbbb9+ovS9rKxR3qertpFAM3OEJr/+Dh/8yy7jK5G5EFPIrfsw/7Q5987ERPIJIH6BspK2CBB2tgcg==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.13.5': - resolution: {integrity: sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==} + '@swc/core-linux-arm-gnueabihf@1.6.5': + resolution: {integrity: sha512-B0g/dROCE747RRegs/jPHuKJgwXLracDhnqQa80kFdgWEMjlcb7OMCgs5OX86yJGRS4qcYbiMGD0Pp7Kbqn3yw==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.13.5': - resolution: {integrity: sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==} + '@swc/core-linux-arm64-gnu@1.6.5': + resolution: {integrity: sha512-W8meapgXTq8AOtSvDG4yKR8ant2WWD++yOjgzAleB5VAC+oC+aa8YJROGxj8HepurU8kurqzcialwoMeq5SZZQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.13.5': - resolution: {integrity: sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==} + '@swc/core-linux-arm64-musl@1.6.5': + resolution: {integrity: sha512-jyCKqoX50Fg8rJUQqh4u5PqnE7nqYKXHjVH2WcYr114/MU21zlsI+YL6aOQU1XP8bJQ2gPQ1rnlnGJdEHiKS/w==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.13.5': - resolution: {integrity: sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==} + '@swc/core-linux-x64-gnu@1.6.5': + resolution: {integrity: sha512-G6HmUn/RRIlXC0YYFfBz2qh6OZkHS/KUPkhoG4X9ADcgWXXjOFh6JrefwsYj8VBAJEnr5iewzjNfj+nztwHaeA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.13.5': - resolution: {integrity: sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==} + '@swc/core-linux-x64-musl@1.6.5': + resolution: {integrity: sha512-AQpBjBnelQDSbeTJA50AXdS6+CP66LsXIMNTwhPSgUfE7Bx1ggZV11Fsi4Q5SGcs6a8Qw1cuYKN57ZfZC5QOuA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.13.5': - resolution: {integrity: sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==} + '@swc/core-win32-arm64-msvc@1.6.5': + resolution: {integrity: sha512-MZTWM8kUwS30pVrtbzSGEXtek46aXNb/mT9D6rsS7NvOuv2w+qZhjR1rzf4LNbbn5f8VnR4Nac1WIOYZmfC5ng==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.13.5': - resolution: {integrity: sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==} + '@swc/core-win32-ia32-msvc@1.6.5': + resolution: {integrity: sha512-WZdu4gISAr3yOm1fVwKhhk6+MrP7kVX0KMP7+ZQFTN5zXQEiDSDunEJKVgjMVj3vlR+6mnAqa/L0V9Qa8+zKlQ==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.13.5': - resolution: {integrity: sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==} + '@swc/core-win32-x64-msvc@1.6.5': + resolution: {integrity: sha512-ezXgucnMTzlFIxQZw7ls/5r2hseFaRoDL04cuXUOs97E8r+nJSmFsRQm/ygH5jBeXNo59nyZCalrjJAjwfgACA==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.13.5': - resolution: {integrity: sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==} + '@swc/core@1.6.5': + resolution: {integrity: sha512-tyVvUK/HDOUUsK6/GmWvnqUtD9oDpPUA4f7f7JCOV8hXxtfjMtAZeBKf93yrB1XZet69TDR7EN0hFC6i4MF0Ig==} engines: {node: '>=10'} peerDependencies: - '@swc/helpers': '>=0.5.17' + '@swc/helpers': '*' peerDependenciesMeta: '@swc/helpers': optional: true @@ -1148,131 +1221,131 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/types@0.1.24': - resolution: {integrity: sha512-tjTMh3V4vAORHtdTprLlfoMptu1WfTZG9Rsca6yOKyNYsRr+MUXutKmliB17orgSZk5DpnDxs8GUdd/qwYxOng==} + '@swc/types@0.1.9': + resolution: {integrity: sha512-qKnCno++jzcJ4lM4NTfYifm1EFSCeIfKiAHAfkENZAV5Kl9PjJIyd2yeeVv6c/2CckuLyv2NmRC5pv6pm2WQBg==} '@szmarczak/http-timer@5.0.1': resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} engines: {node: '>=14.16'} - '@tailwindcss/forms@0.5.10': - resolution: {integrity: sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==} + '@tailwindcss/forms@0.5.9': + resolution: {integrity: sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg==} peerDependencies: - tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1' + tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20' - '@tailwindcss/typography@0.5.16': - resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==} + '@tailwindcss/typography@0.5.15': + resolution: {integrity: sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==} peerDependencies: - tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20' - '@tanstack/query-core@5.87.1': - resolution: {integrity: sha512-HOFHVvhOCprrWvtccSzc7+RNqpnLlZ5R6lTmngb8aq7b4rc2/jDT0w+vLdQ4lD9bNtQ+/A4GsFXy030Gk4ollA==} + '@tanstack/query-core@5.48.0': + resolution: {integrity: sha512-lZAfPPeVIqXCswE9SSbG33B6/91XOWt/Iq41bFeWb/mnHwQSIfFRbkS4bfs+WhIk9abRArF9Id2fp0Mgo+hq6Q==} - '@tanstack/react-query@5.87.1': - resolution: {integrity: sha512-YKauf8jfMowgAqcxj96AHs+Ux3m3bWT1oSVKamaRPXSnW2HqSznnTCEkAVqctF1e/W9R/mPcyzzINIgpOH94qg==} + '@tanstack/react-query@5.48.0': + resolution: {integrity: sha512-GDExbjYWzvDokyRqMSWXdrPiYpp95Aig0oeMIrxTaruOJJgWiWfUP//OAaowm2RrRkGVsavSZdko/XmIrrV2Nw==} peerDependencies: - react: ^18 || ^19 + react: ^18.0.0 - '@tauri-apps/api@2.8.0': - resolution: {integrity: sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==} + '@tauri-apps/api@2.0.2': + resolution: {integrity: sha512-3wSwmG+1kr6WrgAFKK5ijkNFPp8TT3FLj3YHUb5EwMO+3FxX4uWlfSWkeeBy+Kc1RsKzugtYLuuya+98Flj+3w==} - '@tauri-apps/cli-darwin-arm64@2.8.4': - resolution: {integrity: sha512-BKu8HRkYV01SMTa7r4fLx+wjgtRK8Vep7lmBdHDioP6b8XH3q2KgsAyPWfEZaZIkZ2LY4SqqGARaE9oilNe0oA==} + '@tauri-apps/api@2.6.0': + resolution: {integrity: sha512-hRNcdercfgpzgFrMXWwNDBN0B7vNzOzRepy6ZAmhxi5mDLVPNrTpo9MGg2tN/F7JRugj4d2aF7E1rtPXAHaetg==} + + '@tauri-apps/api@2.9.0': + resolution: {integrity: sha512-qD5tMjh7utwBk9/5PrTA/aGr3i5QaJ/Mlt7p8NilQ45WgbifUNPyKWsA63iQ8YfQq6R8ajMapU+/Q8nMcPRLNw==} + + '@tauri-apps/cli-darwin-arm64@2.0.3': + resolution: {integrity: sha512-jIsbxGWS+As1ZN7umo90nkql/ZAbrDK0GBT6UsgHSz5zSwwArICsZFFwE1pLZip5yoiV5mn3TGG2c1+v+0puzQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tauri-apps/cli-darwin-x64@2.8.4': - resolution: {integrity: sha512-imb9PfSd/7G6VAO7v1bQ2A3ZH4NOCbhGJFLchxzepGcXf9NKkfun157JH9mko29K6sqAwuJ88qtzbKCbWJTH9g==} + '@tauri-apps/cli-darwin-x64@2.0.3': + resolution: {integrity: sha512-ROITHtLTA1muyrwgyuwyasmaLCGtT4as/Kd1kerXaSDtFcYrnxiM984ZD0+FDUEDl5BgXtYa/sKKkKQFjgmM0A==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tauri-apps/cli-linux-arm-gnueabihf@2.8.4': - resolution: {integrity: sha512-Ml215UnDdl7/fpOrF1CNovym/KjtUbCuPgrcZ4IhqUCnhZdXuphud/JT3E8X97Y03TZ40Sjz8raXYI2ET0exzw==} + '@tauri-apps/cli-linux-arm-gnueabihf@2.0.3': + resolution: {integrity: sha512-bQ3EZwCFfrLg/ZQ2I8sLuifSxESz4TP56SleTkKsPtTIZgNnKpM88PRDz4neiRroHVOq8NK0X276qi9LjGcXPw==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tauri-apps/cli-linux-arm64-gnu@2.8.4': - resolution: {integrity: sha512-pbcgBpMyI90C83CxE5REZ9ODyIlmmAPkkJXtV398X3SgZEIYy5TACYqlyyv2z5yKgD8F8WH4/2fek7+jH+ZXAw==} + '@tauri-apps/cli-linux-arm64-gnu@2.0.3': + resolution: {integrity: sha512-aLfAA8P9OTErVUk3sATxtXqpAtlfDPMPp4fGjDysEELG/MyekGhmh2k/kG/i32OdPeCfO+Nr37wJksARJKubGw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-arm64-musl@2.8.4': - resolution: {integrity: sha512-zumFeaU1Ws5Ay872FTyIm7z8kfzEHu8NcIn8M6TxbJs0a7GRV21KBdpW1zNj2qy7HynnpQCqjAYXTUUmm9JAOw==} + '@tauri-apps/cli-linux-arm64-musl@2.0.3': + resolution: {integrity: sha512-I4MVD7nf6lLLRmNQPpe5beEIFM6q7Zkmh77ROA5BNu/+vHNL5kiTMD+bmd10ZL2r753A6pO7AvqkIxcBuIl0tg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-riscv64-gnu@2.8.4': - resolution: {integrity: sha512-qiqbB3Zz6IyO201f+1ojxLj65WYj8mixL5cOMo63nlg8CIzsP23cPYUrx1YaDPsCLszKZo7tVs14pc7BWf+/aQ==} - engines: {node: '>= 10'} - cpu: [riscv64] - os: [linux] - - '@tauri-apps/cli-linux-x64-gnu@2.8.4': - resolution: {integrity: sha512-TaqaDd9Oy6k45Hotx3pOf+pkbsxLaApv4rGd9mLuRM1k6YS/aw81YrsMryYPThrxrScEIUcmNIHaHsLiU4GMkw==} + '@tauri-apps/cli-linux-x64-gnu@2.0.3': + resolution: {integrity: sha512-C6Jkx2zZGKkoi+sg5FK9GoH/0EvAaOgrZfF5azV5EALGba46g7VpWcZgp9zFUd7K2IzTi+0OOY8TQ2OVfKZgew==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-linux-x64-musl@2.8.4': - resolution: {integrity: sha512-ot9STAwyezN8w+bBHZ+bqSQIJ0qPZFlz/AyscpGqB/JnJQVDFQcRDmUPFEaAtt2UUHSWzN3GoTJ5ypqLBp2WQA==} + '@tauri-apps/cli-linux-x64-musl@2.0.3': + resolution: {integrity: sha512-qi4ghmTfSAl+EEUDwmwI9AJUiOLNSmU1RgiGgcPRE+7A/W+Am9UnxYySAiRbB/gJgTl9sj/pqH5Y9duP1/sqHg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tauri-apps/cli-win32-arm64-msvc@2.8.4': - resolution: {integrity: sha512-+2aJ/g90dhLiOLFSD1PbElXX3SoMdpO7HFPAZB+xot3CWlAZD1tReUFy7xe0L5GAR16ZmrxpIDM9v9gn5xRy/w==} + '@tauri-apps/cli-win32-arm64-msvc@2.0.3': + resolution: {integrity: sha512-UXxHkYmFesC97qVmZre4vY7oDxRDtC2OeKNv0bH+iSnuUp/ROxzJYGyaelnv9Ybvgl4YVqDCnxgB28qMM938TA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tauri-apps/cli-win32-ia32-msvc@2.8.4': - resolution: {integrity: sha512-yj7WDxkL1t9Uzr2gufQ1Hl7hrHuFKTNEOyascbc109EoiAqCp0tgZ2IykQqOZmZOHU884UAWI1pVMqBhS/BfhA==} + '@tauri-apps/cli-win32-ia32-msvc@2.0.3': + resolution: {integrity: sha512-D+xoaa35RGlkXDpnL5uDTpj29untuC5Wp6bN9snfgFDagD0wnFfC8+2ZQGu16bD0IteWqDI0OSoIXhNvy+F+wg==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@tauri-apps/cli-win32-x64-msvc@2.8.4': - resolution: {integrity: sha512-XuvGB4ehBdd7QhMZ9qbj/8icGEatDuBNxyYHbLKsTYh90ggUlPa/AtaqcC1Fo69lGkTmq9BOKrs1aWSi7xDonA==} + '@tauri-apps/cli-win32-x64-msvc@2.0.3': + resolution: {integrity: sha512-eWV9XWb4dSYHXl13OtYWLjX1JHphUEkHkkGwJrhr8qFBm7RbxXxQvrsUEprSi51ug/dwJenjJgM4zR8By4htfw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tauri-apps/cli@2.8.4': - resolution: {integrity: sha512-ejUZBzuQRcjFV+v/gdj/DcbyX/6T4unZQjMSBZwLzP/CymEjKcc2+Fc8xTORThebHDUvqoXMdsCZt8r+hyN15g==} + '@tauri-apps/cli@2.0.3': + resolution: {integrity: sha512-JwEyhc5BAVpn4E8kxzY/h7+bVOiXQdudR1r3ODMfyyumZBfgIWqpD/WuTcPq6Yjchju1BSS+80jAE/oYwI/RKg==} engines: {node: '>= 10'} hasBin: true - '@tauri-apps/plugin-dialog@2.4.0': - resolution: {integrity: sha512-OvXkrEBfWwtd8tzVCEXIvRfNEX87qs2jv6SqmVPiHcJjBhSF/GUvjqUNIDmKByb5N8nvDqVUM7+g1sXwdC/S9w==} + '@tauri-apps/plugin-dialog@2.0.0': + resolution: {integrity: sha512-ApNkejXP2jpPBSifznPPcHTXxu9/YaRW+eJ+8+nYwqp0lLUtebFHG4QhxitM43wwReHE81WAV1DQ/b+2VBftOA==} - '@tauri-apps/plugin-fs@2.4.2': - resolution: {integrity: sha512-YGhmYuTgXGsi6AjoV+5mh2NvicgWBfVJHHheuck6oHD+HC9bVWPaHvCP0/Aw4pHDejwrvT8hE3+zZAaWf+hrig==} + '@tauri-apps/plugin-fs@2.4.1': + resolution: {integrity: sha512-vJlKZVGF3UAFGoIEVT6Oq5L4HGDCD78WmA4uhzitToqYiBKWAvZR61M6zAyQzHqLs0ADemkE4RSy/5sCmZm6ZQ==} - '@tauri-apps/plugin-http@2.5.2': - resolution: {integrity: sha512-x1mQKHSLDk4mS2S938OTeyk8L7QyLpCrKZCZcjkljGsvTvRMojCvI9SeJ1kaxc7t8xSilkC7WdId8xER9TIGLg==} + '@tauri-apps/plugin-http@2.5.0': + resolution: {integrity: sha512-l4M2DUIsOBIMrbj4dJZwrB4mJiB7OA/2Tj3gEbX2fjq5MOpETklJPKfDvzUTDwuq4lIKCKKykz8E8tpOgvi0EQ==} - '@tauri-apps/plugin-log@2.7.0': - resolution: {integrity: sha512-81XQ2f93x4vmIB5OY0XlYAxy60cHdYLs0Ki8Qp38tNATRiuBit+Orh3frpY3qfYQnqEvYVyRub7YRJWlmW2RRA==} + '@tauri-apps/plugin-log@2.7.1': + resolution: {integrity: sha512-jdb+o0wxQc8PjnLktgGpOs9Dh1YupaOGDXzO+Y8peA1UZ1ep3eXv4E1oiJ7nIQVN0XUFDDhnn3aBszl8ijhR+A==} - '@tauri-apps/plugin-opener@2.5.0': - resolution: {integrity: sha512-B0LShOYae4CZjN8leiNDbnfjSrTwoZakqKaWpfoH6nXiJwt6Rgj6RnVIffG3DoJiKsffRhMkjmBV9VeilSb4TA==} + '@tauri-apps/plugin-opener@2.4.0': + resolution: {integrity: sha512-43VyN8JJtvKWJY72WI/KNZszTpDpzHULFxQs0CJBIYUdCRowQ6Q1feWTDb979N7nldqSuDOaBupZ6wz2nvuWwQ==} - '@tauri-apps/plugin-os@2.3.1': - resolution: {integrity: sha512-ty5V8XDUIFbSnrk3zsFoP3kzN+vAufYzalJSlmrVhQTImIZa1aL1a03bOaP2vuBvfR+WDRC6NgV2xBl8G07d+w==} + '@tauri-apps/plugin-os@2.0.0': + resolution: {integrity: sha512-M7hG/nNyQYTJxVG/UhTKhp9mpXriwWzrs9mqDreB8mIgqA3ek5nHLdwRZJWhkKjZrnDT4v9CpA9BhYeplTlAiA==} - '@tauri-apps/plugin-shell@2.3.1': - resolution: {integrity: sha512-jjs2WGDO/9z2pjNlydY/F5yYhNsscv99K5lCmU5uKjsVvQ3dRlDhhtVYoa4OLDmktLtQvgvbQjCFibMl6tgGfw==} + '@tauri-apps/plugin-shell@2.3.0': + resolution: {integrity: sha512-6GIRxO2z64uxPX4CCTuhQzefvCC0ew7HjdBhMALiGw74vFBDY95VWueAHOHgNOMV4UOUAFupyidN9YulTe5xlA==} - '@tauri-apps/plugin-store@2.4.0': - resolution: {integrity: sha512-PjBnlnH6jyI71MGhrPaxUUCsOzc7WO1mbc4gRhME0m2oxLgCqbksw6JyeKQimuzv4ysdpNO3YbmaY2haf82a3A==} + '@tauri-apps/plugin-store@2.4.1': + resolution: {integrity: sha512-ckGSEzZ5Ii4Hf2D5x25Oqnm2Zf9MfDWAzR+volY0z/OOBz6aucPKEY0F649JvQ0Vupku6UJo7ugpGRDOFOunkA==} - '@tweenjs/tween.js@23.1.3': - resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + '@tweenjs/tween.js@23.1.2': + resolution: {integrity: sha512-kMCNaZCJugWI86xiEHaY338CU5JpD0B97p1j1IKNn/Zto8PgACjQx0UxbHjmOcLl/dDOBnItwD07KmCs75pxtQ==} '@tweenjs/tween.js@25.0.0': resolution: {integrity: sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==} @@ -1280,20 +1353,23 @@ packages: '@twemoji/svg@15.0.0': resolution: {integrity: sha512-ZSPef2B6nBaYnfgdTbAy4jgW95o7pi2xPGwGCU+WMTxo7J6B1lMPTWwSq/wTuiMq+N0khQ90CcvYp1wFoQpo/w==} - '@tybys/wasm-util@0.10.0': - resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + + '@types/debounce@1.2.4': + resolution: {integrity: sha512-jBqiORIzKDOToaF63Fm//haOCHuwQuLa2202RK4MozpA6lh93eCBc+/8+wZn5OzjJt3ySdc+74SXWXB55Ewtyw==} '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -1304,8 +1380,8 @@ packages: '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} '@types/file-saver@2.0.7': resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==} @@ -1328,19 +1404,17 @@ packages: '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - '@types/node@24.3.1': - resolution: {integrity: sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==} + '@types/node@24.10.0': + resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==} '@types/offscreencanvas@2019.7.3': resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==} - '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + '@types/prop-types@15.7.12': + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - '@types/react-dom@18.3.7': - resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} - peerDependencies: - '@types/react': ^18.0.0 + '@types/react-dom@18.3.0': + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} '@types/react-helmet@6.1.11': resolution: {integrity: sha512-0QcdGLddTERotCXo3VFlUSWO3ztraw8nZ6e3zJSgG7apwV5xt+pJUS8ewPBqT4NYB1optGLprNQzFleIY84u/g==} @@ -1351,19 +1425,17 @@ packages: '@types/react-reconciler@0.26.7': resolution: {integrity: sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==} - '@types/react-reconciler@0.28.9': - resolution: {integrity: sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==} - peerDependencies: - '@types/react': '*' + '@types/react-reconciler@0.28.8': + resolution: {integrity: sha512-SN9c4kxXZonFhbX4hJrZy37yw9e7EIxcpHCxQv5JUS18wDE5ovkQKlqQEkufdJCCMfuI9BnjUJvhYeJ9x5Ra7g==} - '@types/react@18.3.24': - resolution: {integrity: sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==} + '@types/react@18.3.11': + resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==} - '@types/semver@7.7.1': - resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - '@types/stats.js@0.17.4': - resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} + '@types/stats.js@0.17.3': + resolution: {integrity: sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==} '@types/three@0.163.0': resolution: {integrity: sha512-uIdDhsXRpQiBUkflBS/i1l3JX14fW6Ot9csed60nfbZNXHDTRsnV2xnTVwXcgbvTiboAR4IW+t+lTL5f1rqIqA==} @@ -1374,8 +1446,11 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/webxr@0.5.23': - resolution: {integrity: sha512-GPe4AsfOSpqWd3xA/0gwoKod13ChcfV67trvxaW2krUbgb9gxQjnCx8zGshzMl8LSHZlNH5gQ8LNScsDuc7nGQ==} + '@types/webxr@0.5.16': + resolution: {integrity: sha512-0E0Cl84FECtzrB4qG19TNTqpunw0F1YF0QZZnFMF6pDw1kNKJtrlTKlVB34stGIsHbZsYQ7H0tNjPfZftkHHoA==} + + '@types/yoga-layout@1.9.2': + resolution: {integrity: sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw==} '@typescript-eslint/eslint-plugin@7.18.0': resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} @@ -1388,11 +1463,11 @@ packages: typescript: optional: true - '@typescript-eslint/eslint-plugin@8.46.2': - resolution: {integrity: sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==} + '@typescript-eslint/eslint-plugin@8.46.4': + resolution: {integrity: sha512-R48VhmTJqplNyDxCyqqVkFSZIx1qX6PzwqgcXn1olLrzxcSBDlOsbtcnQuQhNtnNiJ4Xe5gREI1foajYaYU2Vg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.46.2 + '@typescript-eslint/parser': ^8.46.4 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' @@ -1406,15 +1481,15 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.46.2': - resolution: {integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==} + '@typescript-eslint/parser@8.46.4': + resolution: {integrity: sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.46.2': - resolution: {integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==} + '@typescript-eslint/project-service@8.46.4': + resolution: {integrity: sha512-nPiRSKuvtTN+no/2N1kt2tUh/HoFzeEgOm9fQ6XQk4/ApGqjx0zFIIaLJ6wooR1HIoozvj2j6vTi/1fgAz7UYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -1423,12 +1498,12 @@ packages: resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/scope-manager@8.46.2': - resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==} + '@typescript-eslint/scope-manager@8.46.4': + resolution: {integrity: sha512-tMDbLGXb1wC+McN1M6QeDx7P7c0UWO5z9CXqp7J8E+xGcJuUuevWKxuG8j41FoweS3+L41SkyKKkia16jpX7CA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.46.2': - resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==} + '@typescript-eslint/tsconfig-utils@8.46.4': + resolution: {integrity: sha512-+/XqaZPIAk6Cjg7NWgSGe27X4zMGqrFqZ8atJsX3CWxH/jACqWnrWI68h7nHQld0y+k9eTTjb9r+KU4twLoo9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -1443,8 +1518,8 @@ packages: typescript: optional: true - '@typescript-eslint/type-utils@8.46.2': - resolution: {integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==} + '@typescript-eslint/type-utils@8.46.4': + resolution: {integrity: sha512-V4QC8h3fdT5Wro6vANk6eojqfbv5bpwHuMsBcJUJkqs2z5XnYhJzyz9Y02eUmF9u3PgXEUiOt4w4KHR3P+z0PQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1454,8 +1529,8 @@ packages: resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/types@8.46.2': - resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==} + '@typescript-eslint/types@8.46.4': + resolution: {integrity: sha512-USjyxm3gQEePdUwJBFjjGNG18xY9A2grDVGuk7/9AkjIF1L+ZrVnwR5VAU5JXtUnBL/Nwt3H31KlRDaksnM7/w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@7.18.0': @@ -1467,8 +1542,8 @@ packages: typescript: optional: true - '@typescript-eslint/typescript-estree@8.46.2': - resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==} + '@typescript-eslint/typescript-estree@8.46.4': + resolution: {integrity: sha512-7oV2qEOr1d4NWNmpXLR35LvCfOkTNymY9oyW+lUHkmCno7aOmIf/hMaydnJBUTBMRCOGZh8YjkFOc8dadEoNGA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' @@ -1479,8 +1554,8 @@ packages: peerDependencies: eslint: ^8.56.0 - '@typescript-eslint/utils@8.46.2': - resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==} + '@typescript-eslint/utils@8.46.4': + resolution: {integrity: sha512-AbSv11fklGXV6T28dp2Me04Uw90R2iJ30g2bgLz529Koehrmkbs1r7paFqr1vPCZi7hHwYxYtxfyQMRC8QaVSg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1490,12 +1565,12 @@ packages: resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/visitor-keys@8.46.2': - resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==} + '@typescript-eslint/visitor-keys@8.46.4': + resolution: {integrity: sha512-/++5CYLQqsO9HFGLI7APrxBJYo+5OCMpViuhV8q5/Qa3o5mMrF//eQHks+PXcsAVaLdn817fMuS7zqoXNNZGaw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} '@unrs/resolver-binding-android-arm-eabi@1.11.1': resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} @@ -1600,11 +1675,11 @@ packages: peerDependencies: react: '>= 16.8.0' - '@vitejs/plugin-react@4.7.0': - resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + '@vitejs/plugin-react@4.3.2': + resolution: {integrity: sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + vite: ^4.2.0 || ^5.0.0 '@wry/caches@1.0.1': resolution: {integrity: sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==} @@ -1618,6 +1693,10 @@ packages: resolution: {integrity: sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==} engines: {node: '>=8'} + '@wry/trie@0.4.3': + resolution: {integrity: sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==} + engines: {node: '>=8'} + '@wry/trie@0.5.0': resolution: {integrity: sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==} engines: {node: '>=8'} @@ -1627,6 +1706,11 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn@8.12.0: + resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==} + engines: {node: '>=0.4.0'} + hasBin: true + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -1642,6 +1726,10 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + ansi-escapes@7.0.0: resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} engines: {node: '>=18'} @@ -1650,10 +1738,14 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.2.0: - resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==} + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1745,16 +1837,16 @@ packages: ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - async-function@1.0.0: - resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} - engines: {node: '>= 0.4'} + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} - auto-bind@5.0.1: - resolution: {integrity: sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + auto-bind@4.0.0: + resolution: {integrity: sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==} + engines: {node: '>=8'} - autoprefixer@10.4.21: - resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -1797,8 +1889,8 @@ packages: brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -1807,8 +1899,13 @@ packages: browser-fs-access@0.35.0: resolution: {integrity: sha512-sLoadumpRfsjprP8XzVjpQc0jK8yqHBx0PtUTGYj2fftT+P/t+uyDAQdMgGAPKD011in/O+YYGh7fIs0oG/viw==} - browserslist@4.25.4: - resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==} + browserslist@4.23.1: + resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + browserslist@4.24.0: + resolution: {integrity: sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1860,13 +1957,16 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - camera-controls@2.10.1: - resolution: {integrity: sha512-KnaKdcvkBJ1Irbrzl8XD6WtZltkRjp869Jx8c0ujs9K+9WD+1D7ryBsCiVqJYUqt6i/HR5FxT7RLASieUD+Q5w==} + camera-controls@2.8.5: + resolution: {integrity: sha512-7VTwRk7Nu1nRKsY7bEt9HVBfKt8DETvzyYhLN4OW26OByBayMDB5fUaNcPI+z++vG23RH5yqn6ZRhZcgLQy2rA==} peerDependencies: three: '>=0.126.1' - caniuse-lite@1.0.30001741: - resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==} + caniuse-lite@1.0.30001636: + resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==} + + caniuse-lite@1.0.30001669: + resolution: {integrity: sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==} case@1.6.3: resolution: {integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==} @@ -1875,12 +1975,16 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.6.0: - resolution: {integrity: sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==} + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} character-entities-html4@2.1.0: @@ -1903,16 +2007,19 @@ packages: resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==} engines: {node: '>= 14.16.0'} + ci-info@2.0.0: + resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - cli-boxes@3.0.0: - resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} - engines: {node: '>=10'} + cli-boxes@2.2.1: + resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} + engines: {node: '>=6'} - cli-cursor@4.0.0: - resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} @@ -1923,6 +2030,10 @@ packages: engines: {node: '>=8.0.0', npm: '>=5.0.0'} hasBin: true + cli-truncate@2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + cli-truncate@4.0.0: resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} engines: {node: '>=18'} @@ -1939,14 +2050,20 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - code-excerpt@4.0.0: - resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + code-excerpt@3.0.0: + resolution: {integrity: sha512-VHNTVhd7KsLGOqfX3SyeO8RyYPMp1GJOg194VITk04WMYCv4plV68YWe6TJZxd9MhobjtpMRnVky01gqZsalaw==} + engines: {node: '>=10'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -1956,8 +2073,8 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - commander@13.1.0: - resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} commander@2.20.3: @@ -1976,9 +2093,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - convert-to-spaces@2.0.1: - resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + convert-to-spaces@1.0.2: + resolution: {integrity: sha512-cj09EBuObp9gZNQCzc7hByQyrs6jVGE+o9kSJmeUoj+GiPiJvi5LYqEH/Hmme4+MTLHM+Ejtq+FChpjjEnsPdQ==} + engines: {node: '>= 4'} create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -1988,6 +2105,10 @@ packages: engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} hasBin: true + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -2030,6 +2151,9 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} + debounce@1.2.1: + resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -2038,8 +2162,17 @@ packages: supports-color: optional: true - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -2059,9 +2192,6 @@ packages: decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} - decode-named-character-reference@1.2.0: - resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} - decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -2101,8 +2231,8 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - detect-gpu@5.0.70: - resolution: {integrity: sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==} + detect-gpu@5.0.38: + resolution: {integrity: sha512-36QeGHSXYcJ/RfrnPEScR8GDprbXFG4ZhXsfVNVHztZr38+fRxgHnJl3CjYXXjbeRUhu3ZZBJh6Lg0A9v0Qd8A==} detect-libc@1.0.3: resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} @@ -2130,8 +2260,8 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} draco3d@1.5.7: @@ -2151,11 +2281,14 @@ packages: resolution: {integrity: sha512-uW2UKSsuty9ANJ3YByIQE4ANkD8nqUPO7r6Fwcc1ADKPe9FRdcPpMl3VEput4JSvKBJ4J86npIC2MLP0pYkCuw==} hasBin: true - electron-to-chromium@1.5.214: - resolution: {integrity: sha512-TpvUNdha+X3ybfU78NoQatKvQEm1oq3lf2QbnmCEdw+Bd9RuIAY+hJTvq1avzHM0f7EJfnH3vbCnbzKzisc/9Q==} + electron-to-chromium@1.4.803: + resolution: {integrity: sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g==} - emoji-regex@10.5.0: - resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==} + electron-to-chromium@1.5.41: + resolution: {integrity: sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ==} + + emoji-regex@10.3.0: + resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -2228,9 +2361,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - es-toolkit@1.39.10: - resolution: {integrity: sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==} - es6-promise@3.3.1: resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} @@ -2247,6 +2377,10 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} @@ -2353,8 +2487,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.0: - resolution: {integrity: sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==} + eslint@9.39.1: + resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -2367,8 +2501,8 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -2406,8 +2540,8 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} fast-json-parse@1.0.3: @@ -2451,8 +2585,8 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - find-babel-config@2.1.2: - resolution: {integrity: sha512-ZfZp1rQyp4gyuxqt1ZqjFGVeVBvmpURMqdIWXbPRfB97Bf6BzdK/xSIbylEINzQ0kB5tlDQfn9HkNXXWsqTqLg==} + find-babel-config@2.1.1: + resolution: {integrity: sha512-5Ji+EAysHGe1OipH7GN4qDjok5Z1uw5KAwDCbicU/4wyTZY7CqOCzcWbG7J5ad9mazq67k89fXlbc1MuIfl9uA==} find-up@3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} @@ -2472,8 +2606,8 @@ packages: flatbuffers@22.10.26: resolution: {integrity: sha512-sdO3emf/BlLfOogW6KwHuXg16APR/E86jNacDXfSInPzt8SSEzxlHcqDekfM/IJ1CGC5bvDksfNufNhS8h1FRA==} - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -2482,8 +2616,8 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} engines: {node: '>=14'} form-data-encoder@2.1.4: @@ -2527,10 +2661,13 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.3.1: - resolution: {integrity: sha512-R1QfovbPsKmosqTnPoRFiJ7CF9MLRgb53ChvMZm+r4p76/+8yKDy17qLL2PKInORy2RkZZekuK0efYgmzTkXyQ==} + get-east-asian-width@1.2.0: + resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} engines: {node: '>=18'} + get-intrinsic@1.2.1: + resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} + get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} @@ -2563,8 +2700,8 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.10.1: - resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -2574,20 +2711,25 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + glob@10.4.1: + resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==} + engines: {node: '>=16 || 14 >=14.18'} hasBin: true glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} engines: {node: '>=16 || 14 >=14.17'} + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + globals@15.10.0: + resolution: {integrity: sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==} engines: {node: '>=18'} globalthis@1.0.4: @@ -2611,7 +2753,6 @@ packages: got-fetch@5.1.10: resolution: {integrity: sha512-Gwj/A2htjvLEcY07PKDItv0WCPEs3dV2vWeZ+9TVBSKSTuWEZ4oXaMD0ZAOsajwx2orahQWN4HI0MfRyWSZsbg==} engines: {node: '>=14.0.0'} - deprecated: please use built-in fetch in nodejs peerDependencies: got: ^12.0.0 @@ -2631,16 +2772,16 @@ packages: peerDependencies: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - graphql@15.10.1: - resolution: {integrity: sha512-BL/Xd/T9baO6NFzoMpiMD7YUZ62R6viR5tp/MULVEnbYJXZA//kRNW7J0j1w/wXArgL0sCxhDfK5dczSKn3+cg==} + graphql@15.9.0: + resolution: {integrity: sha512-GCOQdvm7XxV1S4U4CGrsdlEN37245eC8P9zaYCMr6K1BG0IPGy5lUwmJsEOGyl1GD6HXjOtl2keCP9asRBwNvA==} engines: {node: '>= 10.x'} has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - has-bigints@1.1.0: - resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} - engines: {node: '>= 0.4'} + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -2673,12 +2814,16 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hast-util-to-jsx-runtime@2.3.6: - resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + hast-util-to-jsx-runtime@2.3.2: + resolution: {integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==} hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} @@ -2686,8 +2831,8 @@ packages: highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - hls.js@1.6.11: - resolution: {integrity: sha512-tdDwOAgPGXohSiNE4oxGr3CI9Hx9lsGLFe6TULUvRk2TfHS+w1tSAJntrvxsHaxvjtr6BXsDZM7NOqJFhU4mmg==} + hls.js@1.5.17: + resolution: {integrity: sha512-wA66nnYFvQa1o4DO/BFgLNRKnBTVXpNeldGRBJ2Y0SvFtdwvFKCbqa9zhHoZLoxHhZ+jYsj3aIBkWQQCPNOhMw==} hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} @@ -2695,8 +2840,8 @@ packages: html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} - http-cache-semantics@4.2.0: - resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} http2-client@1.3.5: resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==} @@ -2717,8 +2862,8 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} - husky@9.1.7: - resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + husky@9.1.6: + resolution: {integrity: sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==} engines: {node: '>=18'} hasBin: true @@ -2728,8 +2873,8 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} ignore@7.0.5: @@ -2742,33 +2887,30 @@ packages: immer@10.1.1: resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} - immutable@5.1.3: - resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} + immutable@4.3.6: + resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==} - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - indent-string@5.0.0: - resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} - engines: {node: '>=12'} + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} - ink@5.2.1: - resolution: {integrity: sha512-BqcUyWrG9zq5HIwW6JcfFHsIYebJkWWb4fczNah1goUO0vv5vneIlfwuS85twyJ5hYR/y18FlAYUxrO9ChIWVg==} - engines: {node: '>=18'} + ink@3.2.0: + resolution: {integrity: sha512-firNp1q3xxTzoItj/eOOSZQnYSlyrWks5llCTVX37nJ59K3eXbQ8PtzCguqo8YI19EELo5QxaKnJd4VxzhU8tg==} + engines: {node: '>=10'} peerDependencies: - '@types/react': '>=18.0.0' - react: '>=18.0.0' - react-devtools-core: ^4.19.1 + '@types/react': '>=16.8.0' + react: '>=16.8.0' peerDependenciesMeta: '@types/react': optional: true - react-devtools-core: - optional: true inline-style-parser@0.2.4: resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} @@ -2784,8 +2926,8 @@ packages: intl-pluralrules@2.0.1: resolution: {integrity: sha512-astxTLzIdXPeN0K9Rumi6LfMpm3rvNO0iJE+h/k8Kr/is+wPbRe4ikyDjlLr6VTh/mEfNv8RjN+gu3KwDiuhqg==} - ip-num@1.5.2: - resolution: {integrity: sha512-CUxHEJuJk74GIQA75LD9RDkpK/3NJv8yRnseOh79AlRmFwT1HgYPKDOiVkd0QVR5MNwMj9EnOnS1yJn9CaYNZA==} + ip-num@1.5.1: + resolution: {integrity: sha512-QziFxgxq3mjIf5CuwlzXFYscHxgLqdEdJKRo2UJ5GurL5zrSRMzT/O+nK0ABimoFH8MWF8YwIiwECYsHc1LpUQ==} is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -2805,8 +2947,8 @@ packages: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} - is-async-function@2.1.1: - resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} engines: {node: '>= 0.4'} is-bigint@1.0.4: @@ -2835,6 +2977,17 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} + is-ci@2.0.0: + resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} + hasBin: true + + is-core-module@2.13.1: + resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} @@ -2867,6 +3020,9 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + is-finalizationregistry@1.1.1: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} @@ -2879,12 +3035,12 @@ packages: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} engines: {node: '>=12'} - is-fullwidth-code-point@5.1.0: - resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} engines: {node: '>=18'} - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} is-glob@4.0.3: @@ -2894,11 +3050,6 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-in-ci@1.0.0: - resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==} - engines: {node: '>=18'} - hasBin: true - is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -3012,26 +3163,21 @@ packages: peerDependencies: react: '>=18.0' - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@3.4.0: + resolution: {integrity: sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==} + engines: {node: '>=14'} jiti@1.21.6: resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true - jotai@2.13.1: - resolution: {integrity: sha512-cRsw6kFeGC9Z/D3egVKrTXRweycZ4z/k7i2MrfCzPYsL9SIWcPXTyqv258/+Ay8VUEcihNiE/coBLE6Kic6b8A==} + jotai@2.12.2: + resolution: {integrity: sha512-oN8715y7MkjXlSrpyjlR887TOuc/NLZMs9gvgtfWH/JP47ChwO0lR2ijSwBvPMYyXRAPT+liIAhuBavluKGgtA==} engines: {node: '>=12.20.0'} peerDependencies: - '@babel/core': '>=7.0.0' - '@babel/template': '>=7.0.0' '@types/react': '>=17.0.0' react: '>=17.0.0' peerDependenciesMeta: - '@babel/core': - optional: true - '@babel/template': - optional: true '@types/react': optional: true react: @@ -3044,8 +3190,13 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} engines: {node: '>=6'} hasBin: true @@ -3070,8 +3221,8 @@ packages: engines: {node: '>=6'} hasBin: true - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} @@ -3098,20 +3249,24 @@ packages: lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} - lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} engines: {node: '>=14'} lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - lint-staged@15.5.2: - resolution: {integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==} + lint-staged@15.2.10: + resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==} engines: {node: '>=18.12.0'} hasBin: true - listr2@8.3.3: - resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} + listr2@8.2.5: + resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} engines: {node: '>=18.0.0'} locate-path@3.0.0: @@ -3149,17 +3304,18 @@ packages: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@10.2.2: + resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} + engines: {node: 14 || >=16.14} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - maath@0.10.8: - resolution: {integrity: sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==} + maath@0.10.7: + resolution: {integrity: sha512-zQ2xd7dNOIVTjAS+hj22fyj1EFYmOJX6tzKjZ92r6WDoq8hyFxjuGA2q950tmR4iC/EKXoMQdSipkaJVuUHDTg==} peerDependencies: - '@types/three': '>=0.134.0' - three: '>=0.134.0' + '@types/three': '>=0.144.0' + three: '>=0.144.0' magic-string@0.30.8: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} @@ -3205,8 +3361,8 @@ packages: mdast-util-mdx-expression@2.0.1: resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} - mdast-util-mdx-jsx@3.2.0: - resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + mdast-util-mdx-jsx@3.1.3: + resolution: {integrity: sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==} mdast-util-mdxjs-esm@2.0.1: resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} @@ -3280,9 +3436,6 @@ packages: micromark-util-character@2.1.0: resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - micromark-util-chunked@2.0.0: resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} @@ -3301,9 +3454,6 @@ packages: micromark-util-encode@2.0.0: resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - micromark-util-html-tag-name@2.0.0: resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} @@ -3316,24 +3466,15 @@ packages: micromark-util-sanitize-uri@2.0.0: resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - micromark-util-subtokenize@2.0.1: resolution: {integrity: sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==} micromark-util-symbol@2.0.0: resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - micromark-util-types@2.0.0: resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} - micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - micromark@4.0.0: resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} @@ -3376,8 +3517,8 @@ packages: resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + minimatch@9.0.4: + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: @@ -3391,19 +3532,22 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - napi-postinstall@0.3.3: - resolution: {integrity: sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==} + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} hasBin: true @@ -3429,8 +3573,11 @@ packages: node-readfiles@0.2.0: resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -3440,8 +3587,8 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - normalize-url@8.0.2: - resolution: {integrity: sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw==} + normalize-url@8.0.1: + resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} engines: {node: '>=14.16'} npm-run-path@4.0.1: @@ -3548,8 +3695,8 @@ packages: openapi3-ts@2.0.2: resolution: {integrity: sha512-TxhYBMoqx9frXyOgnRHufjQfPXomTIHYKhSKJ6jHfj13kS8OEIhvmE8CTuQyKtjjWttAjX5DPxM1vmalEpo8Qw==} - optimism@0.18.1: - resolution: {integrity: sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==} + optimism@0.18.0: + resolution: {integrity: sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} @@ -3583,15 +3730,12 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-entities@4.0.2: - resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-entities@4.0.1: + resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} @@ -3602,9 +3746,9 @@ packages: parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - patch-console@2.0.0: - resolution: {integrity: sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + patch-console@1.0.0: + resolution: {integrity: sha512-nxl9nrnLQmh64iTzMfyylSlRozL7kAXIaxw1fVcLYdyhNkJCRUzirRZTikXGJsg+hc4fqpneTK6iU2H1Q8THSA==} + engines: {node: '>=10'} path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} @@ -3638,6 +3782,9 @@ packages: engines: {node: '>=0.10'} hasBin: true + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -3658,8 +3805,8 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} pkg-up@3.1.0: @@ -3674,10 +3821,6 @@ packages: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - postcss-import@15.1.0: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} @@ -3702,8 +3845,8 @@ packages: ts-node: optional: true - postcss-nested@6.2.0: - resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + postcss-nested@6.0.1: + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} engines: {node: '>=12.0'} peerDependencies: postcss: ^8.2.14 @@ -3712,15 +3855,19 @@ packages: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} engines: {node: '>=4'} - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + postcss-selector-parser@6.1.0: + resolution: {integrity: sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==} engines: {node: '>=4'} postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} potpack@1.0.2: @@ -3730,8 +3877,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} engines: {node: '>=14'} hasBin: true @@ -3752,8 +3899,8 @@ packages: property-expr@2.0.6: resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} - property-information@7.1.0: - resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -3777,13 +3924,16 @@ packages: peerDependencies: react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-devtools-core@4.28.5: + resolution: {integrity: sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==} + react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: react: ^18.3.1 - react-error-boundary@4.1.2: - resolution: {integrity: sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==} + react-error-boundary@4.0.13: + resolution: {integrity: sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==} peerDependencies: react: '>=16.13.1' @@ -3795,8 +3945,8 @@ packages: peerDependencies: react: '>=16.3.0' - react-hook-form@7.65.0: - resolution: {integrity: sha512-xtOzDz063WcXvGWaHgLNrNzlsdFgtUWcb32E6WFaGTd7kPZG3EeDusjdZfUsPwKCKVXy1ZlntifaHZ4l8pAsmw==} + react-hook-form@7.66.0: + resolution: {integrity: sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -3807,17 +3957,24 @@ packages: react-lifecycles-compat@3.0.4: resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} - react-markdown@9.1.0: - resolution: {integrity: sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==} + react-markdown@9.0.1: + resolution: {integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==} peerDependencies: '@types/react': '>=18' react: '>=18' - react-modal@3.16.3: - resolution: {integrity: sha512-yCYRJB5YkeQDQlTt17WGAgFJ7jr2QYcWa1SHqZ3PluDmnKJ/7+tVU+E6uKyZ0nODaeEj+xCpK4LcSnKXLMC0Nw==} + react-modal@3.16.1: + resolution: {integrity: sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==} + engines: {node: '>=8'} peerDependencies: - react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 - react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 + react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 + react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 + + react-reconciler@0.26.2: + resolution: {integrity: sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q==} + engines: {node: '>=0.10.0'} + peerDependencies: + react: ^17.0.2 react-reconciler@0.27.0: resolution: {integrity: sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==} @@ -3825,31 +3982,25 @@ packages: peerDependencies: react: ^18.0.0 - react-reconciler@0.29.2: - resolution: {integrity: sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==} - engines: {node: '>=0.10.0'} - peerDependencies: - react: ^18.3.1 - - react-refresh@0.17.0: - resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} - react-responsive@10.0.1: - resolution: {integrity: sha512-OM5/cRvbtUWEX8le8RCT8scA8y2OPtb0Q/IViEyCEM5FBN8lRrkUOZnu87I88A6njxDldvxG+rLBxWiA7/UM9g==} + react-responsive@10.0.0: + resolution: {integrity: sha512-N6/UiRLGQyGUqrarhBZmrSmHi2FXSD++N5VbSKsBBvWfG0ZV7asvUBluSv5lSzdMyEVjzZ6Y8DL4OHABiztDOg==} engines: {node: '>=14'} peerDependencies: react: '>=16.8.0' - react-router-dom@6.30.1: - resolution: {integrity: sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==} + react-router-dom@6.27.0: + resolution: {integrity: sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' react-dom: '>=16.8' - react-router@6.30.1: - resolution: {integrity: sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==} + react-router@6.27.0: + resolution: {integrity: sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==} engines: {node: '>=14.0.0'} peerDependencies: react: '>=16.8' @@ -3859,15 +4010,6 @@ packages: peerDependencies: react: ^16.3.0 || ^17.0.0 || ^18.0.0 - react-use-measure@2.1.7: - resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==} - peerDependencies: - react: '>=16.13' - react-dom: '>=16.13' - peerDependenciesMeta: - react-dom: - optional: true - react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -3887,9 +4029,16 @@ packages: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} + reflect.getprototypeof@1.0.6: + resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} + engines: {node: '>= 0.4'} + reftools@1.1.9: resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + regexp.prototype.flags@1.5.2: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} @@ -3909,14 +4058,14 @@ packages: react: optional: true - remark-gfm@4.0.1: - resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + remark-gfm@4.0.0: + resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==} remark-parse@11.0.0: resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} - remark-rehype@11.1.2: - resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + remark-rehype@11.1.1: + resolution: {integrity: sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==} remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} @@ -3942,11 +4091,6 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} - engines: {node: '>= 0.4'} - hasBin: true - resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -3955,48 +4099,49 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + response-iterator@0.2.6: + resolution: {integrity: sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==} + engines: {node: '>=0.8'} + responselike@3.0.0: resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} engines: {node: '>=14.16'} - restore-cursor@4.0.0: - resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup-plugin-visualizer@5.14.0: - resolution: {integrity: sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==} - engines: {node: '>=18'} + rollup-plugin-visualizer@5.12.0: + resolution: {integrity: sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ==} + engines: {node: '>=14'} hasBin: true peerDependencies: - rolldown: 1.x rollup: 2.x || 3.x || 4.x peerDependenciesMeta: - rolldown: - optional: true rollup: optional: true - rollup@4.50.0: - resolution: {integrity: sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==} + rollup@4.24.0: + resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} safe-array-concat@1.1.2: resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} @@ -4018,11 +4163,14 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} - sass@1.92.0: - resolution: {integrity: sha512-KDNI0BxgIRDAfJgzNm5wuy+4yOCIZyrUbjSpiU/JItfih+KGXAVefKL53MTml054MmBA3DDKIBMSI/7XLxZJ3A==} + sass@1.80.2: + resolution: {integrity: sha512-9wXY8cGBlUmoUoT+vwOZOFCiS+naiWVjqlreN9ar9PudXbGwlMTFwCR5K9kB4dFumJ6ib98wZyAObJKsWf1nAA==} engines: {node: '>=14.0.0'} hasBin: true + scheduler@0.20.2: + resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==} + scheduler@0.21.0: resolution: {integrity: sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==} @@ -4033,8 +4181,13 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} hasBin: true @@ -4061,6 +4214,9 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + should-equal@2.0.0: resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==} @@ -4117,6 +4273,10 @@ packages: resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} engines: {node: '>=12'} + slice-ansi@3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + slice-ansi@5.0.0: resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} engines: {node: '>=12'} @@ -4140,9 +4300,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} @@ -4156,8 +4316,8 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-license-ids@3.0.22: - resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + spdx-license-ids@3.0.18: + resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} spdx-ranges@2.1.1: resolution: {integrity: sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==} @@ -4172,11 +4332,8 @@ packages: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} - stats-gl@2.4.2: - resolution: {integrity: sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==} - peerDependencies: - '@types/three': '*' - three: '*' + stats-gl@2.2.8: + resolution: {integrity: sha512-94G5nZvduDmzxBS7K0lYnynYwreZpkknD8g5dZmU6mpwIhy3caCrjAm11Qm1cbyx7mqix7Fp00RkbsonzKWnoQ==} stats.js@0.17.0: resolution: {integrity: sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==} @@ -4201,8 +4358,8 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + string-width@7.1.0: + resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} engines: {node: '>=18'} string.prototype.includes@2.0.1: @@ -4259,17 +4416,18 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - style-to-js@1.1.17: - resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} - - style-to-object@1.0.9: - resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} + style-to-object@1.0.8: + resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==} sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -4294,8 +4452,8 @@ packages: tailwind-gradient-mask-image@1.2.0: resolution: {integrity: sha512-tUJaGhvqbJFiVKJu6EU5n//KvGdVvY3L3VOFNqjztk13+ifAk00pcSNHBTgHfUiBGOEzDn0gFRbSmsftUV1lXA==} - tailwindcss@3.4.17: - resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + tailwindcss@3.4.14: + resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==} engines: {node: '>=14.0.0'} hasBin: true @@ -4317,8 +4475,8 @@ packages: peerDependencies: three: '>= 0.151.0' - three-stdlib@2.36.0: - resolution: {integrity: sha512-kv0Byb++AXztEGsULgMAs8U2jgUdz6HPpAB/wDJnLiLlaWQX2APHhiTJIN7rqW+Of0eRgcp7jn05U1BsCP3xBA==} + three-stdlib@2.30.3: + resolution: {integrity: sha512-rYr8PqMljMza+Ct8kQk90Y7y+YcWoPu1thfYv5YGCp0hytNRbxSQWXY4GpdTGymCj3bDggEBpxso53C3pPwhIw==} peerDependencies: three: '>=0.128.0' @@ -4331,10 +4489,14 @@ packages: tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -4348,24 +4510,24 @@ packages: trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - troika-three-text@0.52.4: - resolution: {integrity: sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==} + troika-three-text@0.49.1: + resolution: {integrity: sha512-lXGWxgjJP9kw4i4Wh+0k0Q/7cRfS6iOME4knKht/KozPu9GcFA9NnNpRvehIhrUawq9B0ZRw+0oiFHgRO+4Wig==} peerDependencies: three: '>=0.125.0' - troika-three-utils@0.52.4: - resolution: {integrity: sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==} + troika-three-utils@0.49.0: + resolution: {integrity: sha512-umitFL4cT+Fm/uONmaQEq4oZlyRHWwVClaS6ZrdcueRvwc2w+cpNQ47LlJKJswpqtMFWbEhOLy0TekmcPZOdYA==} peerDependencies: three: '>=0.125.0' - troika-worker-utils@0.52.0: - resolution: {integrity: sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==} + troika-worker-utils@0.49.0: + resolution: {integrity: sha512-1xZHoJrG0HFfCvT/iyN41DvI/nRykiBtHqFkGaGgJwq5iXfIZFBiPPEHFpPpgyKM3Oo5ITHXP5wM2TNQszYdVg==} trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - ts-api-utils@1.4.3: - resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' @@ -4390,8 +4552,8 @@ packages: peerDependencies: typescript: '>=2.7' - ts-pattern@5.8.0: - resolution: {integrity: sha512-kIjN2qmWiHnhgr5DAkAafF9fwb0T5OhMVSWrm8XEdTFnX6+wfXwYOFjeF86UZ54vduqiR7BfqScFmXSzSaH8oA==} + ts-pattern@5.5.0: + resolution: {integrity: sha512-jqbIpTsa/KKTJYWgPNsFNbLVpwCgzXfFJ1ukNn4I8hMwyQzHMJnk/BqWzggB0xpkILuKzaO/aMYhS0SkaJyKXg==} tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -4399,8 +4561,8 @@ packages: tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} tsutils@3.21.0: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -4418,14 +4580,18 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-fest@0.12.0: + resolution: {integrity: sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + type-fest@2.19.0: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - typed-array-buffer@1.0.2: resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} engines: {node: '>= 0.4'} @@ -4458,8 +4624,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.46.2: - resolution: {integrity: sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==} + typescript-eslint@8.46.4: + resolution: {integrity: sha512-KALyxkpYV5Ix7UhvjTwJXZv76VWsHG+NjNlt/z+a17SOQSiOcBdUXdbJdyXi7RPxrBFECtFOiPwUJQusJuCqrg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -4475,8 +4641,8 @@ packages: engines: {node: '>=4.2.0'} hasBin: true - typescript@5.9.2: - resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + typescript@5.6.3: + resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} engines: {node: '>=14.17'} hasBin: true @@ -4487,8 +4653,8 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - undici-types@7.10.0: - resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -4518,8 +4684,14 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + update-browserslist-db@1.0.16: + resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + update-browserslist-db@1.1.1: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -4527,15 +4699,15 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - use-double-tap@1.3.7: - resolution: {integrity: sha512-OOgpAtT1U96D8jX6w/ODWhLMDpS92wVYIQyQQ0hDnOgLrPHNrkYSjTnkoPT41GaO30xF5OBJ7mAxF34sHxnLng==} + use-double-tap@1.3.6: + resolution: {integrity: sha512-zWmzlihSTuLJpT+YJqhVUySV8UNvmdmaXokBEIh+FxR4m/vaSk2cS5hlqEPDj64rmkHlL7zfRTrJPw30Jd0OZA==} peerDependencies: react: '>=16.8.0' - use-sync-external-store@1.5.0: - resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + use-sync-external-store@1.2.0: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -4544,14 +4716,18 @@ packages: resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} engines: {node: '>= 4'} - vfile-message@4.0.3: - resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite@5.4.19: - resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} + vite@5.4.9: + resolution: {integrity: sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -4593,8 +4769,8 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webpack-sources@3.3.3: - resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + webpack-sources@3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} webpack-virtual-modules@0.5.0: @@ -4610,6 +4786,10 @@ packages: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} + which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + which-builtin-type@1.2.1: resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} engines: {node: '>= 0.4'} @@ -4631,14 +4811,18 @@ packages: engines: {node: '>= 8'} hasBin: true - widest-line@5.0.0: - resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} - engines: {node: '>=18'} + widest-line@3.1.0: + resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} + engines: {node: '>=8'} word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -4654,12 +4838,12 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.3: - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} - engines: {node: '>=10.0.0'} + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' + utf-8-validate: ^5.0.2 peerDependenciesMeta: bufferutil: optional: true @@ -4677,9 +4861,14 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - yaml@2.8.1: - resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} - engines: {node: '>= 14.6'} + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + + yaml@2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + engines: {node: '>= 14'} hasBin: true yargs-parser@20.2.9: @@ -4706,11 +4895,12 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yoga-layout@3.2.1: - resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + yoga-layout-prebuilt@1.10.0: + resolution: {integrity: sha512-YnOmtSbv4MTf7RGJMK0FvZ+KD8OEe/J5BNnR0GHhD8J/XcG/Qvxgszm0Un6FTHWW4uHlTgP0IztiXQnGyIR45g==} + engines: {node: '>=8'} - yup@1.7.0: - resolution: {integrity: sha512-VJce62dBd+JQvoc+fCVq+KZfPHr+hXaxCcVgotfwWvlR0Ja3ffYKaJBT8rptPOSKOGJDCUnW2C2JWpud7aRP6Q==} + yup@1.4.0: + resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==} zen-observable-ts@1.2.5: resolution: {integrity: sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==} @@ -4727,8 +4917,8 @@ packages: react: optional: true - zustand@4.5.7: - resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + zustand@4.5.2: + resolution: {integrity: sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==} engines: {node: '>=12.7.0'} peerDependencies: '@types/react': '>=16.8' @@ -4742,51 +4932,34 @@ packages: react: optional: true - zustand@5.0.8: - resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=18.0.0' - immer: '>=9.0.6' - react: '>=18.0.0' - use-sync-external-store: '>=1.2.0' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true - use-sync-external-store: - optional: true - zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} snapshots: - '@alcalzone/ansi-tokenize@0.1.3': - dependencies: - ansi-styles: 6.2.1 - is-fullwidth-code-point: 4.0.0 - '@alloc/quick-lru@5.2.0': {} - '@apollo/client@3.14.0(@types/react@18.3.24)(graphql@15.10.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@ampproject/remapping@2.3.0': dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@15.10.1) + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@apollo/client@3.10.6(@types/react@18.3.11)(graphql@15.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@15.9.0) '@wry/caches': 1.0.1 '@wry/equality': 0.5.7 '@wry/trie': 0.5.0 - graphql: 15.10.1 - graphql-tag: 2.12.6(graphql@15.10.1) + graphql: 15.9.0 + graphql-tag: 2.12.6(graphql@15.9.0) hoist-non-react-statics: 3.3.2 - optimism: 0.18.1 + optimism: 0.18.0 prop-types: 15.8.1 - rehackt: 0.1.0(@types/react@18.3.24)(react@18.3.1) + rehackt: 0.1.0(@types/react@18.3.11)(react@18.3.1) + response-iterator: 0.2.6 symbol-observable: 4.0.0 ts-invariant: 0.10.3 - tslib: 2.8.1 + tslib: 2.6.3 zen-observable-ts: 1.2.5 optionalDependencies: react: 18.3.1 @@ -4794,133 +4967,280 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@babel/code-frame@7.27.1': + '@babel/code-frame@7.24.7': dependencies: - '@babel/helper-validator-identifier': 7.27.1 - js-tokens: 4.0.0 - picocolors: 1.1.1 + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 - '@babel/compat-data@7.28.4': {} - - '@babel/core@7.28.4': + '@babel/code-frame@7.25.7': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.4 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 - '@jridgewell/remapping': 2.3.5 + '@babel/highlight': 7.25.7 + picocolors: 1.0.1 + + '@babel/compat-data@7.24.7': {} + + '@babel/compat-data@7.25.8': {} + + '@babel/core@7.24.7': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) + '@babel/helpers': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 convert-source-map: 2.0.0 - debug: 4.4.1 + debug: 4.3.5 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.28.3': + '@babel/core@7.25.8': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 - jsesc: 3.1.0 + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.25.7 + '@babel/generator': 7.25.7 + '@babel/helper-compilation-targets': 7.25.7 + '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.8) + '@babel/helpers': 7.25.7 + '@babel/parser': 7.25.8 + '@babel/template': 7.25.7 + '@babel/traverse': 7.25.7 + '@babel/types': 7.25.8 + convert-source-map: 2.0.0 + debug: 4.3.5 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color - '@babel/helper-compilation-targets@7.27.2': + '@babel/generator@7.24.7': dependencies: - '@babel/compat-data': 7.28.4 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.25.4 + '@babel/types': 7.24.7 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/generator@7.25.7': + dependencies: + '@babel/types': 7.25.8 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.0.2 + + '@babel/helper-compilation-targets@7.24.7': + dependencies: + '@babel/compat-data': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + browserslist: 4.23.1 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-globals@7.28.0': {} - - '@babel/helper-module-imports@7.27.1': + '@babel/helper-compilation-targets@7.25.7': dependencies: - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/compat-data': 7.25.8 + '@babel/helper-validator-option': 7.25.7 + browserslist: 4.24.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-environment-visitor@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-function-name@7.24.7': + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/helper-hoist-variables@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-module-imports@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + '@babel/helper-module-imports@7.25.7': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.25.7 + '@babel/types': 7.25.8 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.27.1': {} - - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.27.1': {} - - '@babel/helper-validator-option@7.27.1': {} - - '@babel/helpers@7.28.4': + '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.28.4 - - '@babel/parser@7.28.4': - dependencies: - '@babel/types': 7.28.4 - - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/runtime@7.28.4': {} - - '@babel/template@7.27.2': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - - '@babel/traverse@7.28.4': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.4 - '@babel/template': 7.27.2 - '@babel/types': 7.28.4 - debug: 4.4.3 + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 transitivePeerDependencies: - supports-color - '@babel/types@7.28.4': + '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.8)': dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 + '@babel/core': 7.25.8 + '@babel/helper-module-imports': 7.25.7 + '@babel/helper-simple-access': 7.25.7 + '@babel/helper-validator-identifier': 7.25.7 + '@babel/traverse': 7.25.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.24.7': {} + + '@babel/helper-simple-access@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-simple-access@7.25.7': + dependencies: + '@babel/traverse': 7.25.7 + '@babel/types': 7.25.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-split-export-declaration@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/helper-string-parser@7.24.7': {} + + '@babel/helper-string-parser@7.25.7': {} + + '@babel/helper-validator-identifier@7.24.7': {} + + '@babel/helper-validator-identifier@7.25.7': {} + + '@babel/helper-validator-option@7.24.7': {} + + '@babel/helper-validator-option@7.25.7': {} + + '@babel/helpers@7.24.7': + dependencies: + '@babel/template': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/helpers@7.25.7': + dependencies: + '@babel/template': 7.25.7 + '@babel/types': 7.25.8 + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + + '@babel/highlight@7.25.7': + dependencies: + '@babel/helper-validator-identifier': 7.25.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + + '@babel/parser@7.24.7': + dependencies: + '@babel/types': 7.24.7 + + '@babel/parser@7.25.8': + dependencies: + '@babel/types': 7.25.8 + + '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@babel/helper-plugin-utils': 7.24.7 + + '@babel/runtime@7.24.7': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.24.7': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + + '@babel/template@7.25.7': + dependencies: + '@babel/code-frame': 7.25.7 + '@babel/parser': 7.25.8 + '@babel/types': 7.25.8 + + '@babel/traverse@7.24.7': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + debug: 4.3.5 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/traverse@7.25.7': + dependencies: + '@babel/code-frame': 7.25.7 + '@babel/generator': 7.25.7 + '@babel/parser': 7.25.8 + '@babel/template': 7.25.7 + '@babel/types': 7.25.8 + debug: 4.3.5 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.24.7': + dependencies: + '@babel/helper-string-parser': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + + '@babel/types@7.25.8': + dependencies: + '@babel/helper-string-parser': 7.25.7 + '@babel/helper-validator-identifier': 7.25.7 + to-fast-properties: 2.0.0 '@dword-design/dedent@0.7.0': dependencies: babel-plugin-add-module-exports: 1.0.4 - '@dword-design/endent@1.4.7': + '@dword-design/endent@1.4.1': dependencies: - dedent: '@dword-design/dedent@0.7.0' + '@dword-design/dedent': 0.7.0 fast-json-parse: 1.0.3 objectorarray: 1.0.5 '@dword-design/eslint-plugin-import-alias@4.0.9': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.24.7 '@dword-design/functions': 5.0.27 babel-plugin-module-resolver: 5.0.2 deepmerge: 4.3.1 @@ -4930,25 +5250,25 @@ snapshots: '@dword-design/functions@5.0.27': dependencies: - '@dword-design/endent': 1.4.7 + '@dword-design/endent': 1.4.1 delay: 5.0.0 lodash: 4.17.21 tinycolor2: 1.6.0 - '@emnapi/core@1.5.0': + '@emnapi/core@1.7.0': dependencies: '@emnapi/wasi-threads': 1.1.0 - tslib: 2.8.1 + tslib: 2.6.3 optional: true - '@emnapi/runtime@1.5.0': + '@emnapi/runtime@1.7.0': dependencies: - tslib: 2.8.1 + tslib: 2.6.3 optional: true '@emnapi/wasi-threads@1.1.0': dependencies: - tslib: 2.8.1 + tslib: 2.6.3 optional: true '@esbuild/aix-ppc64@0.21.5': @@ -5020,17 +5340,24 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.0(jiti@1.21.6))': + '@eslint-community/eslint-utils@4.4.0(eslint@9.39.1(jiti@1.21.6))': dependencies: - eslint: 9.39.0(jiti@1.21.6) + eslint: 9.39.1(jiti@1.21.6) eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@1.21.6))': + dependencies: + eslint: 9.39.1(jiti@1.21.6) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.10.1': {} + '@eslint-community/regexpp@4.12.2': {} '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3 + debug: 4.3.7 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -5046,18 +5373,18 @@ snapshots: '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.3 + debug: 4.3.7 espree: 10.4.0 globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 + ignore: 5.3.1 + import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.39.0': {} + '@eslint/js@9.39.1': {} '@eslint/object-schema@2.1.7': {} @@ -5081,19 +5408,19 @@ snapshots: dependencies: '@fluent/bundle': 0.18.0 - '@fontsource/poppins@5.2.6': {} + '@fontsource/poppins@5.1.0': {} '@formatjs/intl-localematcher@0.2.32': dependencies: - tslib: 2.8.1 + tslib: 2.6.3 - '@graphql-typed-document-node/core@3.2.0(graphql@15.10.1)': + '@graphql-typed-document-node/core@3.2.0(graphql@15.9.0)': dependencies: - graphql: 15.10.1 + graphql: 15.9.0 - '@hookform/resolvers@3.10.0(react-hook-form@7.65.0(react@18.3.1))': + '@hookform/resolvers@3.6.0(react-hook-form@7.66.0(react@18.3.1))': dependencies: - react-hook-form: 7.65.0(react@18.3.1) + react-hook-form: 7.66.0(react@18.3.1) '@humanfs/core@0.19.1': {} @@ -5115,38 +5442,30 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@jridgewell/gen-mapping@0.3.13': + '@jridgewell/gen-mapping@0.3.5': dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.30 - - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/source-map@0.3.11': + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.6': dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 optional: true - '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.4.15': {} - '@jridgewell/trace-mapping@0.3.30': + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - optional: true - - '@mediapipe/tasks-vision@0.10.17': {} + '@mediapipe/tasks-vision@0.10.8': {} '@mgit-at/typescript-flatbuffers-codegen@0.1.3': dependencies: @@ -5160,16 +5479,16 @@ snapshots: typescript: 4.8.4 yargs: 16.2.0 - '@monogrid/gainmap-js@3.1.0(three@0.163.0)': + '@monogrid/gainmap-js@3.0.5(three@0.163.0)': dependencies: promise-worker-transferable: 1.0.4 three: 0.163.0 '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.5.0 - '@emnapi/runtime': 1.5.0 - '@tybys/wasm-util': 0.10.0 + '@emnapi/core': 1.7.0 + '@emnapi/runtime': 1.7.0 + '@tybys/wasm-util': 0.10.1 optional: true '@nodelib/fs.scandir@2.1.5': @@ -5186,26 +5505,26 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} - '@openapi-codegen/cli@2.0.4(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@openapi-codegen/cli@2.0.2(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@apollo/client': 3.14.0(@types/react@18.3.24)(graphql@15.10.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@swc/core': 1.13.5 + '@apollo/client': 3.10.6(@types/react@18.3.11)(graphql@15.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@swc/core': 1.6.5 case: 1.6.3 - chalk: 5.6.0 + chalk: 5.3.0 cli-highlight: 2.1.11 clipanion: 3.2.1(typanion@3.14.0) fs-extra: 10.1.0 got: 12.6.1 got-fetch: 5.1.10(got@12.6.1) - graphql: 15.10.1 - ink: 5.2.1(@types/react@18.3.24)(react@18.3.1) + graphql: 15.9.0 + ink: 3.2.0(@types/react@18.3.11)(react@18.3.1) js-yaml: 4.1.0 openapi3-ts: 2.0.2 - prettier: 3.6.2 - rxjs: 7.8.2 + prettier: 3.3.3 + rxjs: 7.8.1 slash: 4.0.0 swagger2openapi: 7.0.8 - tslib: 2.8.1 + tslib: 2.6.3 typanion: 3.14.0 typescript: 4.8.2 transitivePeerDependencies: @@ -5215,18 +5534,17 @@ snapshots: - encoding - graphql-ws - react - - react-devtools-core - react-dom - subscriptions-transport-ws - utf-8-validate - '@openapi-codegen/typescript@8.1.1': + '@openapi-codegen/typescript@8.0.2': dependencies: case: 1.6.3 lodash: 4.17.21 openapi3-ts: 2.0.2 pluralize: 8.0.0 - tslib: 2.8.1 + tslib: 2.6.3 tsutils: 3.21.0(typescript@4.8.2) typescript: 4.8.2 @@ -5285,204 +5603,186 @@ snapshots: '@parcel/watcher-win32-arm64': 2.4.1 '@parcel/watcher-win32-ia32': 2.4.1 '@parcel/watcher-win32-x64': 2.4.1 - optional: true '@pkgjs/parseargs@0.11.0': optional: true - '@react-hookz/deep-equal@3.0.4': {} + '@react-hookz/deep-equal@3.0.3': {} - '@react-spring/animated@9.7.5(react@18.3.1)': + '@react-spring/animated@9.6.1(react@18.3.1)': dependencies: - '@react-spring/shared': 9.7.5(react@18.3.1) - '@react-spring/types': 9.7.5 + '@react-spring/shared': 9.6.1(react@18.3.1) + '@react-spring/types': 9.6.1 react: 18.3.1 - '@react-spring/core@9.7.5(react@18.3.1)': + '@react-spring/core@9.6.1(react@18.3.1)': dependencies: - '@react-spring/animated': 9.7.5(react@18.3.1) - '@react-spring/shared': 9.7.5(react@18.3.1) - '@react-spring/types': 9.7.5 + '@react-spring/animated': 9.6.1(react@18.3.1) + '@react-spring/rafz': 9.6.1 + '@react-spring/shared': 9.6.1(react@18.3.1) + '@react-spring/types': 9.6.1 react: 18.3.1 - '@react-spring/rafz@9.7.5': {} + '@react-spring/rafz@9.6.1': {} - '@react-spring/shared@9.7.5(react@18.3.1)': + '@react-spring/shared@9.6.1(react@18.3.1)': dependencies: - '@react-spring/rafz': 9.7.5 - '@react-spring/types': 9.7.5 + '@react-spring/rafz': 9.6.1 + '@react-spring/types': 9.6.1 react: 18.3.1 - '@react-spring/three@9.7.5(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(react@18.3.1)(three@0.163.0)': + '@react-spring/three@9.6.1(@react-three/fiber@8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(react@18.3.1)(three@0.163.0)': dependencies: - '@react-spring/animated': 9.7.5(react@18.3.1) - '@react-spring/core': 9.7.5(react@18.3.1) - '@react-spring/shared': 9.7.5(react@18.3.1) - '@react-spring/types': 9.7.5 - '@react-three/fiber': 8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0) + '@react-spring/animated': 9.6.1(react@18.3.1) + '@react-spring/core': 9.6.1(react@18.3.1) + '@react-spring/shared': 9.6.1(react@18.3.1) + '@react-spring/types': 9.6.1 + '@react-three/fiber': 8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0) react: 18.3.1 three: 0.163.0 - '@react-spring/types@9.7.5': {} + '@react-spring/types@9.6.1': {} - '@react-three/drei@9.122.0(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(@types/react@18.3.24)(@types/three@0.163.0)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)(use-sync-external-store@1.5.0(react@18.3.1))': + '@react-three/drei@9.114.5(@react-three/fiber@8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(@types/react@18.3.11)(@types/three@0.163.0)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)': dependencies: - '@babel/runtime': 7.28.4 - '@mediapipe/tasks-vision': 0.10.17 - '@monogrid/gainmap-js': 3.1.0(three@0.163.0) - '@react-spring/three': 9.7.5(@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(react@18.3.1)(three@0.163.0) - '@react-three/fiber': 8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0) + '@babel/runtime': 7.24.7 + '@mediapipe/tasks-vision': 0.10.8 + '@monogrid/gainmap-js': 3.0.5(three@0.163.0) + '@react-spring/three': 9.6.1(@react-three/fiber@8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0))(react@18.3.1)(three@0.163.0) + '@react-three/fiber': 8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0) '@use-gesture/react': 10.3.1(react@18.3.1) - camera-controls: 2.10.1(three@0.163.0) + camera-controls: 2.8.5(three@0.163.0) cross-env: 7.0.3 - detect-gpu: 5.0.70 + detect-gpu: 5.0.38 glsl-noise: 0.0.0 - hls.js: 1.6.11 - maath: 0.10.8(@types/three@0.163.0)(three@0.163.0) + hls.js: 1.5.17 + maath: 0.10.7(@types/three@0.163.0)(three@0.163.0) meshline: 3.3.1(three@0.163.0) react: 18.3.1 react-composer: 5.0.3(react@18.3.1) - stats-gl: 2.4.2(@types/three@0.163.0)(three@0.163.0) + stats-gl: 2.2.8 stats.js: 0.17.0 suspend-react: 0.1.3(react@18.3.1) three: 0.163.0 three-mesh-bvh: 0.7.8(three@0.163.0) - three-stdlib: 2.36.0(three@0.163.0) - troika-three-text: 0.52.4(three@0.163.0) - tunnel-rat: 0.1.2(@types/react@18.3.24)(immer@10.1.1)(react@18.3.1) + three-stdlib: 2.30.3(three@0.163.0) + troika-three-text: 0.49.1(three@0.163.0) + tunnel-rat: 0.1.2(@types/react@18.3.11)(immer@10.1.1)(react@18.3.1) utility-types: 3.11.0 - zustand: 5.0.8(@types/react@18.3.24)(immer@10.1.1)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)) + uuid: 9.0.1 + zustand: 3.7.2(react@18.3.1) optionalDependencies: react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: - '@types/react' - '@types/three' - immer - - use-sync-external-store - '@react-three/fiber@8.18.0(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)': + '@react-three/fiber@8.17.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.163.0)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.24.7 + '@types/debounce': 1.2.4 '@types/react-reconciler': 0.26.7 - '@types/webxr': 0.5.23 + '@types/webxr': 0.5.16 base64-js: 1.5.1 buffer: 6.0.3 - its-fine: 1.2.5(@types/react@18.3.24)(react@18.3.1) + debounce: 1.2.1 + its-fine: 1.2.5(react@18.3.1) react: 18.3.1 react-reconciler: 0.27.0(react@18.3.1) - react-use-measure: 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) scheduler: 0.21.0 suspend-react: 0.1.3(react@18.3.1) three: 0.163.0 zustand: 3.7.2(react@18.3.1) optionalDependencies: react-dom: 18.3.1(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - '@remix-run/router@1.23.0': {} + '@remix-run/router@1.20.0': {} - '@rolldown/pluginutils@1.0.0-beta.27': {} - - '@rollup/rollup-android-arm-eabi@4.50.0': + '@rollup/rollup-android-arm-eabi@4.24.0': optional: true - '@rollup/rollup-android-arm64@4.50.0': + '@rollup/rollup-android-arm64@4.24.0': optional: true - '@rollup/rollup-darwin-arm64@4.50.0': + '@rollup/rollup-darwin-arm64@4.24.0': optional: true - '@rollup/rollup-darwin-x64@4.50.0': + '@rollup/rollup-darwin-x64@4.24.0': optional: true - '@rollup/rollup-freebsd-arm64@4.50.0': + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': optional: true - '@rollup/rollup-freebsd-x64@4.50.0': + '@rollup/rollup-linux-arm-musleabihf@4.24.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.50.0': + '@rollup/rollup-linux-arm64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.50.0': + '@rollup/rollup-linux-arm64-musl@4.24.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.50.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.50.0': + '@rollup/rollup-linux-riscv64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.50.0': + '@rollup/rollup-linux-s390x-gnu@4.24.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.50.0': + '@rollup/rollup-linux-x64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.50.0': + '@rollup/rollup-linux-x64-musl@4.24.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.50.0': + '@rollup/rollup-win32-arm64-msvc@4.24.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.50.0': + '@rollup/rollup-win32-ia32-msvc@4.24.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.50.0': - optional: true - - '@rollup/rollup-linux-x64-musl@4.50.0': - optional: true - - '@rollup/rollup-openharmony-arm64@4.50.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.50.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.50.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.50.0': + '@rollup/rollup-win32-x64-msvc@4.24.0': optional: true '@rtsao/scc@1.1.0': {} - '@sentry-internal/browser-utils@9.46.0': + '@sentry-internal/browser-utils@9.9.0': dependencies: - '@sentry/core': 9.46.0 + '@sentry/core': 9.9.0 - '@sentry-internal/feedback@9.46.0': + '@sentry-internal/feedback@9.9.0': dependencies: - '@sentry/core': 9.46.0 + '@sentry/core': 9.9.0 - '@sentry-internal/replay-canvas@9.46.0': + '@sentry-internal/replay-canvas@9.9.0': dependencies: - '@sentry-internal/replay': 9.46.0 - '@sentry/core': 9.46.0 + '@sentry-internal/replay': 9.9.0 + '@sentry/core': 9.9.0 - '@sentry-internal/replay@9.46.0': + '@sentry-internal/replay@9.9.0': dependencies: - '@sentry-internal/browser-utils': 9.46.0 - '@sentry/core': 9.46.0 + '@sentry-internal/browser-utils': 9.9.0 + '@sentry/core': 9.9.0 - '@sentry/babel-plugin-component-annotate@2.23.1': {} + '@sentry/babel-plugin-component-annotate@2.22.7': {} - '@sentry/browser@9.46.0': + '@sentry/browser@9.9.0': dependencies: - '@sentry-internal/browser-utils': 9.46.0 - '@sentry-internal/feedback': 9.46.0 - '@sentry-internal/replay': 9.46.0 - '@sentry-internal/replay-canvas': 9.46.0 - '@sentry/core': 9.46.0 + '@sentry-internal/browser-utils': 9.9.0 + '@sentry-internal/feedback': 9.9.0 + '@sentry-internal/replay': 9.9.0 + '@sentry-internal/replay-canvas': 9.9.0 + '@sentry/core': 9.9.0 - '@sentry/bundler-plugin-core@2.23.1': + '@sentry/bundler-plugin-core@2.22.7': dependencies: - '@babel/core': 7.28.4 - '@sentry/babel-plugin-component-annotate': 2.23.1 + '@babel/core': 7.25.8 + '@sentry/babel-plugin-component-annotate': 2.22.7 '@sentry/cli': 2.39.1 - dotenv: 16.6.1 + dotenv: 16.4.5 find-up: 5.0.0 glob: 9.3.5 magic-string: 0.30.8 @@ -5531,18 +5831,18 @@ snapshots: - encoding - supports-color - '@sentry/core@9.46.0': {} + '@sentry/core@9.9.0': {} - '@sentry/react@9.46.0(react@18.3.1)': + '@sentry/react@9.9.0(react@18.3.1)': dependencies: - '@sentry/browser': 9.46.0 - '@sentry/core': 9.46.0 + '@sentry/browser': 9.9.0 + '@sentry/core': 9.9.0 hoist-non-react-statics: 3.3.2 react: 18.3.1 - '@sentry/vite-plugin@2.23.1': + '@sentry/vite-plugin@2.22.7': dependencies: - '@sentry/bundler-plugin-core': 2.23.1 + '@sentry/bundler-plugin-core': 2.22.7 unplugin: 1.0.1 transitivePeerDependencies: - encoding @@ -5550,65 +5850,65 @@ snapshots: '@sindresorhus/is@5.6.0': {} - '@stylistic/eslint-plugin@5.5.0(eslint@9.39.0(jiti@1.21.6))': + '@stylistic/eslint-plugin@5.5.0(eslint@9.39.1(jiti@1.21.6))': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0(jiti@1.21.6)) - '@typescript-eslint/types': 8.46.2 - eslint: 9.39.0(jiti@1.21.6) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.6)) + '@typescript-eslint/types': 8.46.4 + eslint: 9.39.1(jiti@1.21.6) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 picomatch: 4.0.3 - '@swc/core-darwin-arm64@1.13.5': + '@swc/core-darwin-arm64@1.6.5': optional: true - '@swc/core-darwin-x64@1.13.5': + '@swc/core-darwin-x64@1.6.5': optional: true - '@swc/core-linux-arm-gnueabihf@1.13.5': + '@swc/core-linux-arm-gnueabihf@1.6.5': optional: true - '@swc/core-linux-arm64-gnu@1.13.5': + '@swc/core-linux-arm64-gnu@1.6.5': optional: true - '@swc/core-linux-arm64-musl@1.13.5': + '@swc/core-linux-arm64-musl@1.6.5': optional: true - '@swc/core-linux-x64-gnu@1.13.5': + '@swc/core-linux-x64-gnu@1.6.5': optional: true - '@swc/core-linux-x64-musl@1.13.5': + '@swc/core-linux-x64-musl@1.6.5': optional: true - '@swc/core-win32-arm64-msvc@1.13.5': + '@swc/core-win32-arm64-msvc@1.6.5': optional: true - '@swc/core-win32-ia32-msvc@1.13.5': + '@swc/core-win32-ia32-msvc@1.6.5': optional: true - '@swc/core-win32-x64-msvc@1.13.5': + '@swc/core-win32-x64-msvc@1.6.5': optional: true - '@swc/core@1.13.5': + '@swc/core@1.6.5': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.24 + '@swc/types': 0.1.9 optionalDependencies: - '@swc/core-darwin-arm64': 1.13.5 - '@swc/core-darwin-x64': 1.13.5 - '@swc/core-linux-arm-gnueabihf': 1.13.5 - '@swc/core-linux-arm64-gnu': 1.13.5 - '@swc/core-linux-arm64-musl': 1.13.5 - '@swc/core-linux-x64-gnu': 1.13.5 - '@swc/core-linux-x64-musl': 1.13.5 - '@swc/core-win32-arm64-msvc': 1.13.5 - '@swc/core-win32-ia32-msvc': 1.13.5 - '@swc/core-win32-x64-msvc': 1.13.5 + '@swc/core-darwin-arm64': 1.6.5 + '@swc/core-darwin-x64': 1.6.5 + '@swc/core-linux-arm-gnueabihf': 1.6.5 + '@swc/core-linux-arm64-gnu': 1.6.5 + '@swc/core-linux-arm64-musl': 1.6.5 + '@swc/core-linux-x64-gnu': 1.6.5 + '@swc/core-linux-x64-musl': 1.6.5 + '@swc/core-win32-arm64-msvc': 1.6.5 + '@swc/core-win32-ia32-msvc': 1.6.5 + '@swc/core-win32-x64-msvc': 1.6.5 '@swc/counter@0.1.3': {} - '@swc/types@0.1.24': + '@swc/types@0.1.9': dependencies: '@swc/counter': 0.1.3 @@ -5616,138 +5916,140 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@tailwindcss/forms@0.5.10(tailwindcss@3.4.17(ts-node@9.1.1(typescript@5.9.2)))': + '@tailwindcss/forms@0.5.9(tailwindcss@3.4.14(ts-node@9.1.1(typescript@5.6.3)))': dependencies: mini-svg-data-uri: 1.4.4 - tailwindcss: 3.4.17(ts-node@9.1.1(typescript@5.9.2)) + tailwindcss: 3.4.14(ts-node@9.1.1(typescript@5.6.3)) - '@tailwindcss/typography@0.5.16(tailwindcss@3.4.17(ts-node@9.1.1(typescript@5.9.2)))': + '@tailwindcss/typography@0.5.15(tailwindcss@3.4.14(ts-node@9.1.1(typescript@5.6.3)))': dependencies: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.17(ts-node@9.1.1(typescript@5.9.2)) + tailwindcss: 3.4.14(ts-node@9.1.1(typescript@5.6.3)) - '@tanstack/query-core@5.87.1': {} + '@tanstack/query-core@5.48.0': {} - '@tanstack/react-query@5.87.1(react@18.3.1)': + '@tanstack/react-query@5.48.0(react@18.3.1)': dependencies: - '@tanstack/query-core': 5.87.1 + '@tanstack/query-core': 5.48.0 react: 18.3.1 - '@tauri-apps/api@2.8.0': {} + '@tauri-apps/api@2.0.2': {} - '@tauri-apps/cli-darwin-arm64@2.8.4': + '@tauri-apps/api@2.6.0': {} + + '@tauri-apps/api@2.9.0': {} + + '@tauri-apps/cli-darwin-arm64@2.0.3': optional: true - '@tauri-apps/cli-darwin-x64@2.8.4': + '@tauri-apps/cli-darwin-x64@2.0.3': optional: true - '@tauri-apps/cli-linux-arm-gnueabihf@2.8.4': + '@tauri-apps/cli-linux-arm-gnueabihf@2.0.3': optional: true - '@tauri-apps/cli-linux-arm64-gnu@2.8.4': + '@tauri-apps/cli-linux-arm64-gnu@2.0.3': optional: true - '@tauri-apps/cli-linux-arm64-musl@2.8.4': + '@tauri-apps/cli-linux-arm64-musl@2.0.3': optional: true - '@tauri-apps/cli-linux-riscv64-gnu@2.8.4': + '@tauri-apps/cli-linux-x64-gnu@2.0.3': optional: true - '@tauri-apps/cli-linux-x64-gnu@2.8.4': + '@tauri-apps/cli-linux-x64-musl@2.0.3': optional: true - '@tauri-apps/cli-linux-x64-musl@2.8.4': + '@tauri-apps/cli-win32-arm64-msvc@2.0.3': optional: true - '@tauri-apps/cli-win32-arm64-msvc@2.8.4': + '@tauri-apps/cli-win32-ia32-msvc@2.0.3': optional: true - '@tauri-apps/cli-win32-ia32-msvc@2.8.4': + '@tauri-apps/cli-win32-x64-msvc@2.0.3': optional: true - '@tauri-apps/cli-win32-x64-msvc@2.8.4': - optional: true - - '@tauri-apps/cli@2.8.4': + '@tauri-apps/cli@2.0.3': optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 2.8.4 - '@tauri-apps/cli-darwin-x64': 2.8.4 - '@tauri-apps/cli-linux-arm-gnueabihf': 2.8.4 - '@tauri-apps/cli-linux-arm64-gnu': 2.8.4 - '@tauri-apps/cli-linux-arm64-musl': 2.8.4 - '@tauri-apps/cli-linux-riscv64-gnu': 2.8.4 - '@tauri-apps/cli-linux-x64-gnu': 2.8.4 - '@tauri-apps/cli-linux-x64-musl': 2.8.4 - '@tauri-apps/cli-win32-arm64-msvc': 2.8.4 - '@tauri-apps/cli-win32-ia32-msvc': 2.8.4 - '@tauri-apps/cli-win32-x64-msvc': 2.8.4 + '@tauri-apps/cli-darwin-arm64': 2.0.3 + '@tauri-apps/cli-darwin-x64': 2.0.3 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.0.3 + '@tauri-apps/cli-linux-arm64-gnu': 2.0.3 + '@tauri-apps/cli-linux-arm64-musl': 2.0.3 + '@tauri-apps/cli-linux-x64-gnu': 2.0.3 + '@tauri-apps/cli-linux-x64-musl': 2.0.3 + '@tauri-apps/cli-win32-arm64-msvc': 2.0.3 + '@tauri-apps/cli-win32-ia32-msvc': 2.0.3 + '@tauri-apps/cli-win32-x64-msvc': 2.0.3 - '@tauri-apps/plugin-dialog@2.4.0': + '@tauri-apps/plugin-dialog@2.0.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.0.2 - '@tauri-apps/plugin-fs@2.4.2': + '@tauri-apps/plugin-fs@2.4.1': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.6.0 - '@tauri-apps/plugin-http@2.5.2': + '@tauri-apps/plugin-http@2.5.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.6.0 - '@tauri-apps/plugin-log@2.7.0': + '@tauri-apps/plugin-log@2.7.1': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.9.0 - '@tauri-apps/plugin-opener@2.5.0': + '@tauri-apps/plugin-opener@2.4.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.6.0 - '@tauri-apps/plugin-os@2.3.1': + '@tauri-apps/plugin-os@2.0.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.0.2 - '@tauri-apps/plugin-shell@2.3.1': + '@tauri-apps/plugin-shell@2.3.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.6.0 - '@tauri-apps/plugin-store@2.4.0': + '@tauri-apps/plugin-store@2.4.1': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.9.0 - '@tweenjs/tween.js@23.1.3': {} + '@tweenjs/tween.js@23.1.2': {} '@tweenjs/tween.js@25.0.0': {} '@twemoji/svg@15.0.0': {} - '@tybys/wasm-util@0.10.0': + '@tybys/wasm-util@0.10.1': dependencies: - tslib: 2.8.1 + tslib: 2.6.3 optional: true '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - '@types/babel__generator': 7.27.0 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 + '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 + '@types/babel__traverse': 7.20.6 - '@types/babel__generator@7.27.0': + '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.24.7 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.24.7 + '@babel/types': 7.24.7 - '@types/babel__traverse@7.28.0': + '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.24.7 + + '@types/debounce@1.2.4': {} '@types/debug@4.1.12': dependencies: @@ -5757,9 +6059,9 @@ snapshots: '@types/estree-jsx@1.0.5': dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.6 - '@types/estree@1.0.8': {} + '@types/estree@1.0.6': {} '@types/file-saver@2.0.7': {} @@ -5779,48 +6081,48 @@ snapshots: '@types/ms@0.7.34': {} - '@types/node@24.3.1': + '@types/node@24.10.0': dependencies: - undici-types: 7.10.0 + undici-types: 7.16.0 '@types/offscreencanvas@2019.7.3': {} - '@types/prop-types@15.7.15': {} + '@types/prop-types@15.7.12': {} - '@types/react-dom@18.3.7(@types/react@18.3.24)': + '@types/react-dom@18.3.0': dependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.11 '@types/react-helmet@6.1.11': dependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.11 '@types/react-modal@3.16.3': dependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.11 '@types/react-reconciler@0.26.7': dependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.11 - '@types/react-reconciler@0.28.9(@types/react@18.3.24)': + '@types/react-reconciler@0.28.8': dependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.11 - '@types/react@18.3.24': + '@types/react@18.3.11': dependencies: - '@types/prop-types': 15.7.15 + '@types/prop-types': 15.7.12 csstype: 3.1.3 - '@types/semver@7.7.1': {} + '@types/semver@7.5.8': {} - '@types/stats.js@0.17.4': {} + '@types/stats.js@0.17.3': {} '@types/three@0.163.0': dependencies: - '@tweenjs/tween.js': 23.1.3 - '@types/stats.js': 0.17.4 - '@types/webxr': 0.5.23 + '@tweenjs/tween.js': 23.1.2 + '@types/stats.js': 0.17.3 + '@types/webxr': 0.5.16 fflate: 0.8.2 meshoptimizer: 0.18.1 @@ -5828,74 +6130,76 @@ snapshots: '@types/unist@3.0.3': {} - '@types/webxr@0.5.23': {} + '@types/webxr@0.5.16': {} - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2))(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2)': + '@types/yoga-layout@1.9.2': {} + + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3))(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) + '@eslint-community/regexpp': 4.10.1 + '@typescript-eslint/parser': 7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) - '@typescript-eslint/utils': 7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) + '@typescript-eslint/type-utils': 7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) '@typescript-eslint/visitor-keys': 7.18.0 - eslint: 9.39.0(jiti@1.21.6) + eslint: 9.39.1(jiti@1.21.6) graphemer: 1.4.0 - ignore: 5.3.2 + ignore: 5.3.1 natural-compare: 1.4.0 - ts-api-utils: 1.4.3(typescript@5.9.2) + ts-api-utils: 1.3.0(typescript@5.6.3) optionalDependencies: - typescript: 5.9.2 + typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2))(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3))(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.46.2 - '@typescript-eslint/type-utils': 8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) - '@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.46.2 - eslint: 9.39.0(jiti@1.21.6) + '@eslint-community/regexpp': 4.10.1 + '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/scope-manager': 8.46.4 + '@typescript-eslint/type-utils': 8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/utils': 8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.46.4 + eslint: 9.39.1(jiti@1.21.6) graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.2) - typescript: 5.9.2 + ts-api-utils: 2.1.0(typescript@5.6.3) + typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2)': + '@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3)': dependencies: '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3) '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.1 - eslint: 9.39.0(jiti@1.21.6) + debug: 4.3.5 + eslint: 9.39.1(jiti@1.21.6) optionalDependencies: - typescript: 5.9.2 + typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2)': + '@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@typescript-eslint/scope-manager': 8.46.2 - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.46.2 - debug: 4.4.3 - eslint: 9.39.0(jiti@1.21.6) - typescript: 5.9.2 + '@typescript-eslint/scope-manager': 8.46.4 + '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.46.4 + debug: 4.3.7 + eslint: 9.39.1(jiti@1.21.6) + typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.46.2(typescript@5.9.2)': + '@typescript-eslint/project-service@8.46.4(typescript@5.6.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.2) - '@typescript-eslint/types': 8.46.2 - debug: 4.4.3 - typescript: 5.9.2 + '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.6.3) + '@typescript-eslint/types': 8.46.4 + debug: 4.3.7 + typescript: 5.6.3 transitivePeerDependencies: - supports-color @@ -5904,93 +6208,93 @@ snapshots: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 - '@typescript-eslint/scope-manager@8.46.2': + '@typescript-eslint/scope-manager@8.46.4': dependencies: - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/visitor-keys': 8.46.2 + '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/visitor-keys': 8.46.4 - '@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.2)': + '@typescript-eslint/tsconfig-utils@8.46.4(typescript@5.6.3)': dependencies: - typescript: 5.9.2 + typescript: 5.6.3 - '@typescript-eslint/type-utils@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2)': + '@typescript-eslint/type-utils@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.2) - '@typescript-eslint/utils': 7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) - debug: 4.4.3 - eslint: 9.39.0(jiti@1.21.6) - ts-api-utils: 1.4.3(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) + debug: 4.3.5 + eslint: 9.39.1(jiti@1.21.6) + ts-api-utils: 1.3.0(typescript@5.6.3) optionalDependencies: - typescript: 5.9.2 + typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2)': + '@typescript-eslint/type-utils@8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.2) - '@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) - debug: 4.4.3 - eslint: 9.39.0(jiti@1.21.6) - ts-api-utils: 2.1.0(typescript@5.9.2) - typescript: 5.9.2 + '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.6.3) + '@typescript-eslint/utils': 8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) + debug: 4.3.7 + eslint: 9.39.1(jiti@1.21.6) + ts-api-utils: 2.1.0(typescript@5.6.3) + typescript: 5.6.3 transitivePeerDependencies: - supports-color '@typescript-eslint/types@7.18.0': {} - '@typescript-eslint/types@8.46.2': {} + '@typescript-eslint/types@8.46.4': {} - '@typescript-eslint/typescript-estree@7.18.0(typescript@5.9.2)': + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.6.3)': dependencies: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.3 + debug: 4.3.5 globby: 11.1.0 is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 1.4.3(typescript@5.9.2) + minimatch: 9.0.4 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.6.3) optionalDependencies: - typescript: 5.9.2 + typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.2)': + '@typescript-eslint/typescript-estree@8.46.4(typescript@5.6.3)': dependencies: - '@typescript-eslint/project-service': 8.46.2(typescript@5.9.2) - '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.2) - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/visitor-keys': 8.46.2 - debug: 4.4.3 - fast-glob: 3.3.3 + '@typescript-eslint/project-service': 8.46.4(typescript@5.6.3) + '@typescript-eslint/tsconfig-utils': 8.46.4(typescript@5.6.3) + '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/visitor-keys': 8.46.4 + debug: 4.3.7 + fast-glob: 3.3.2 is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.9.2) - typescript: 5.9.2 + minimatch: 9.0.4 + semver: 7.6.3 + ts-api-utils: 2.1.0(typescript@5.6.3) + typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2)': + '@typescript-eslint/utils@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0(jiti@1.21.6)) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.39.1(jiti@1.21.6)) '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.2) - eslint: 9.39.0(jiti@1.21.6) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3) + eslint: 9.39.1(jiti@1.21.6) transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/utils@8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2)': + '@typescript-eslint/utils@8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0(jiti@1.21.6)) - '@typescript-eslint/scope-manager': 8.46.2 - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.2) - eslint: 9.39.0(jiti@1.21.6) - typescript: 5.9.2 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.6)) + '@typescript-eslint/scope-manager': 8.46.4 + '@typescript-eslint/types': 8.46.4 + '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.6.3) + eslint: 9.39.1(jiti@1.21.6) + typescript: 5.6.3 transitivePeerDependencies: - supports-color @@ -5999,12 +6303,12 @@ snapshots: '@typescript-eslint/types': 7.18.0 eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@8.46.2': + '@typescript-eslint/visitor-keys@8.46.4': dependencies: - '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/types': 8.46.4 eslint-visitor-keys: 4.2.1 - '@ungap/structured-clone@1.3.0': {} + '@ungap/structured-clone@1.2.0': {} '@unrs/resolver-binding-android-arm-eabi@1.11.1': optional: true @@ -6072,43 +6376,48 @@ snapshots: '@use-gesture/core': 10.3.1 react: 18.3.1 - '@vitejs/plugin-react@4.7.0(vite@5.4.19(@types/node@24.3.1)(sass@1.92.0)(terser@5.31.1))': + '@vitejs/plugin-react@4.3.2(vite@5.4.9(@types/node@24.10.0)(sass@1.80.2)(terser@5.31.1))': dependencies: - '@babel/core': 7.28.4 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.4) - '@rolldown/pluginutils': 1.0.0-beta.27 + '@babel/core': 7.25.8 + '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.8) + '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.8) '@types/babel__core': 7.20.5 - react-refresh: 0.17.0 - vite: 5.4.19(@types/node@24.3.1)(sass@1.92.0)(terser@5.31.1) + react-refresh: 0.14.2 + vite: 5.4.9(@types/node@24.10.0)(sass@1.80.2)(terser@5.31.1) transitivePeerDependencies: - supports-color '@wry/caches@1.0.1': dependencies: - tslib: 2.8.1 + tslib: 2.6.3 '@wry/context@0.7.4': dependencies: - tslib: 2.8.1 + tslib: 2.6.3 '@wry/equality@0.5.7': dependencies: - tslib: 2.8.1 + tslib: 2.6.3 + + '@wry/trie@0.4.3': + dependencies: + tslib: 2.6.3 '@wry/trie@0.5.0': dependencies: - tslib: 2.8.1 + tslib: 2.6.3 acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 + acorn@8.12.0: {} + acorn@8.15.0: {} agent-base@6.0.2: dependencies: - debug: 4.4.3 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -6126,13 +6435,21 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + ansi-escapes@7.0.0: dependencies: environment: 1.1.0 ansi-regex@5.0.1: {} - ansi-regex@6.2.0: {} + ansi-regex@6.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 ansi-styles@4.3.0: dependencies: @@ -6220,7 +6537,7 @@ snapshots: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.24.0 - es-shim-unscopables: 1.1.0 + es-shim-unscopables: 1.0.2 array.prototype.flatmap@1.3.2: dependencies: @@ -6234,7 +6551,7 @@ snapshots: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.24.0 - es-shim-unscopables: 1.1.0 + es-shim-unscopables: 1.0.2 array.prototype.tosorted@1.1.4: dependencies: @@ -6267,18 +6584,18 @@ snapshots: ast-types-flow@0.0.8: {} - async-function@1.0.0: {} + astral-regex@2.0.0: {} - auto-bind@5.0.1: {} + auto-bind@4.0.0: {} - autoprefixer@10.4.21(postcss@8.5.6): + autoprefixer@10.4.20(postcss@8.4.38): dependencies: - browserslist: 4.25.4 - caniuse-lite: 1.0.30001741 + browserslist: 4.24.0 + caniuse-lite: 1.0.30001669 fraction.js: 4.3.7 normalize-range: 0.1.2 - picocolors: 1.1.1 - postcss: 8.5.6 + picocolors: 1.0.1 + postcss: 8.4.38 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: @@ -6293,11 +6610,11 @@ snapshots: babel-plugin-module-resolver@5.0.2: dependencies: - find-babel-config: 2.1.2 + find-babel-config: 2.1.1 glob: 9.3.5 pkg-up: 3.1.0 reselect: 4.1.8 - resolve: 1.22.10 + resolve: 1.22.8 bail@2.0.2: {} @@ -6316,7 +6633,7 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 @@ -6326,12 +6643,19 @@ snapshots: browser-fs-access@0.35.0: {} - browserslist@4.25.4: + browserslist@4.23.1: dependencies: - caniuse-lite: 1.0.30001741 - electron-to-chromium: 1.5.214 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.4) + caniuse-lite: 1.0.30001636 + electron-to-chromium: 1.4.803 + node-releases: 2.0.14 + update-browserslist-db: 1.0.16(browserslist@4.23.1) + + browserslist@4.24.0: + dependencies: + caniuse-lite: 1.0.30001669 + electron-to-chromium: 1.5.41 + node-releases: 2.0.18 + update-browserslist-db: 1.1.1(browserslist@4.24.0) buffer-from@1.1.2: {} @@ -6346,10 +6670,10 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.0.4 get-stream: 6.0.1 - http-cache-semantics: 4.2.0 + http-cache-semantics: 4.1.1 keyv: 4.5.4 mimic-response: 4.0.0 - normalize-url: 8.0.2 + normalize-url: 8.0.1 responselike: 3.0.0 cached-iterable@0.3.0: {} @@ -6362,7 +6686,7 @@ snapshots: call-bind@1.0.2: dependencies: function-bind: 1.1.2 - get-intrinsic: 1.2.4 + get-intrinsic: 1.2.1 call-bind@1.0.7: dependencies: @@ -6375,7 +6699,7 @@ snapshots: call-bind@1.0.8: dependencies: call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 + es-define-property: 1.0.0 get-intrinsic: 1.3.0 set-function-length: 1.2.2 @@ -6390,22 +6714,30 @@ snapshots: camelcase-css@2.0.1: {} - camera-controls@2.10.1(three@0.163.0): + camera-controls@2.8.5(three@0.163.0): dependencies: three: 0.163.0 - caniuse-lite@1.0.30001741: {} + caniuse-lite@1.0.30001636: {} + + caniuse-lite@1.0.30001669: {} case@1.6.3: {} ccount@2.0.1: {} + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.6.0: {} + chalk@5.3.0: {} character-entities-html4@2.1.0: {} @@ -6431,13 +6763,15 @@ snapshots: dependencies: readdirp: 4.0.2 + ci-info@2.0.0: {} + classnames@2.5.1: {} - cli-boxes@3.0.0: {} + cli-boxes@2.2.1: {} - cli-cursor@4.0.0: + cli-cursor@3.1.0: dependencies: - restore-cursor: 4.0.0 + restore-cursor: 3.1.0 cli-cursor@5.0.0: dependencies: @@ -6452,10 +6786,15 @@ snapshots: parse5-htmlparser2-tree-adapter: 6.0.1 yargs: 16.2.0 + cli-truncate@2.1.0: + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.3 + cli-truncate@4.0.0: dependencies: slice-ansi: 5.0.0 - string-width: 7.2.0 + string-width: 7.1.0 clipanion@3.2.1(typanion@3.14.0): dependencies: @@ -6473,21 +6812,27 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - code-excerpt@4.0.0: + code-excerpt@3.0.0: dependencies: - convert-to-spaces: 2.0.1 + convert-to-spaces: 1.0.2 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 color-convert@2.0.1: dependencies: color-name: 1.1.4 + color-name@1.1.3: {} + color-name@1.1.4: {} colorette@2.0.20: {} comma-separated-tokens@2.0.3: {} - commander@13.1.0: {} + commander@12.1.0: {} commander@2.20.3: optional: true @@ -6500,13 +6845,19 @@ snapshots: convert-source-map@2.0.0: {} - convert-to-spaces@2.0.1: {} + convert-to-spaces@1.0.2: {} create-require@1.1.1: {} cross-env@7.0.3: dependencies: - cross-spawn: 7.0.6 + cross-spawn: 7.0.3 + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 cross-spawn@7.0.6: dependencies: @@ -6558,11 +6909,17 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 + debounce@1.2.1: {} + debug@3.2.7: dependencies: ms: 2.1.3 - debug@4.4.1: + debug@4.3.5: + dependencies: + ms: 2.1.2 + + debug@4.3.7: dependencies: ms: 2.1.3 @@ -6574,10 +6931,6 @@ snapshots: dependencies: character-entities: 2.0.2 - decode-named-character-reference@1.2.0: - dependencies: - character-entities: 2.0.2 - decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -6613,7 +6966,7 @@ snapshots: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 - gopd: 1.2.0 + gopd: 1.0.1 define-lazy-prop@2.0.0: {} @@ -6627,12 +6980,11 @@ snapshots: dequal@2.0.3: {} - detect-gpu@5.0.70: + detect-gpu@5.0.38: dependencies: webgl-constants: 1.1.1 - detect-libc@1.0.3: - optional: true + detect-libc@1.0.3: {} devlop@1.1.0: dependencies: @@ -6652,7 +7004,7 @@ snapshots: dependencies: esutils: 2.0.3 - dotenv@16.6.1: {} + dotenv@16.4.5: {} draco3d@1.5.7: {} @@ -6668,9 +7020,11 @@ snapshots: ebnf@1.9.1: {} - electron-to-chromium@1.5.214: {} + electron-to-chromium@1.4.803: {} - emoji-regex@10.5.0: {} + electron-to-chromium@1.5.41: {} + + emoji-regex@10.3.0: {} emoji-regex@8.0.0: {} @@ -6815,7 +7169,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.24.0 es-errors: 1.3.0 - es-set-tostringtag: 2.1.0 + es-set-tostringtag: 2.0.3 function-bind: 1.1.2 get-intrinsic: 1.3.0 globalthis: 1.0.4 @@ -6837,7 +7191,7 @@ snapshots: es-set-tostringtag@2.0.3: dependencies: - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 hasown: 2.0.2 @@ -6865,10 +7219,8 @@ snapshots: es-to-primitive@1.3.0: dependencies: is-callable: 1.2.7 - is-date-object: 1.1.0 - is-symbol: 1.1.1 - - es-toolkit@1.39.10: {} + is-date-object: 1.0.5 + is-symbol: 1.0.4 es6-promise@3.3.1: {} @@ -6902,29 +7254,31 @@ snapshots: escalade@3.2.0: {} + escape-string-regexp@1.0.5: {} + escape-string-regexp@2.0.0: {} escape-string-regexp@4.0.0: {} escape-string-regexp@5.0.0: {} - eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.32.0)(eslint@9.39.0(jiti@1.21.6)): + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.6)): dependencies: confusing-browser-globals: 1.0.11 - eslint: 9.39.0(jiti@1.21.6) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.0(jiti@1.21.6)) + eslint: 9.39.1(jiti@1.21.6) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.6)) object.assign: 4.1.5 object.entries: 1.1.8 semver: 6.3.1 - eslint-config-airbnb@19.0.4(eslint-plugin-import@2.32.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.0(jiti@1.21.6)))(eslint-plugin-react-hooks@4.6.2(eslint@9.39.0(jiti@1.21.6)))(eslint-plugin-react@7.37.5(eslint@9.39.0(jiti@1.21.6)))(eslint@9.39.0(jiti@1.21.6)): + eslint-config-airbnb@19.0.4(eslint-plugin-import@2.32.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@1.21.6)))(eslint-plugin-react-hooks@4.6.2(eslint@9.39.1(jiti@1.21.6)))(eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@1.21.6)))(eslint@9.39.1(jiti@1.21.6)): dependencies: - eslint: 9.39.0(jiti@1.21.6) - eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.32.0)(eslint@9.39.0(jiti@1.21.6)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.0(jiti@1.21.6)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.0(jiti@1.21.6)) - eslint-plugin-react: 7.37.5(eslint@9.39.0(jiti@1.21.6)) - eslint-plugin-react-hooks: 4.6.2(eslint@9.39.0(jiti@1.21.6)) + eslint: 9.39.1(jiti@1.21.6) + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.6)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.6)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@1.21.6)) + eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.6)) + eslint-plugin-react-hooks: 4.6.2(eslint@9.39.1(jiti@1.21.6)) object.assign: 4.1.5 object.entries: 1.1.8 @@ -6936,33 +7290,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.0(jiti@1.21.6)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.6)): dependencies: '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.1 - eslint: 9.39.0(jiti@1.21.6) - get-tsconfig: 4.10.1 + debug: 4.4.3 + eslint: 9.39.1(jiti@1.21.6) + get-tsconfig: 4.13.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.0(jiti@1.21.6)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.6)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.0(jiti@1.21.6)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.6)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) - eslint: 9.39.0(jiti@1.21.6) + '@typescript-eslint/parser': 7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) + eslint: 9.39.1(jiti@1.21.6) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.0(jiti@1.21.6)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@1.21.6)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.0(jiti@1.21.6)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.6)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -6971,9 +7325,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.0(jiti@1.21.6) + eslint: 9.39.1(jiti@1.21.6) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.0(jiti@1.21.6)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@1.21.6)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -6985,13 +7339,13 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.18.0(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) + '@typescript-eslint/parser': 7.18.0(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.0(jiti@1.21.6)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@1.21.6)): dependencies: aria-query: 5.3.2 array-includes: 3.1.8 @@ -7001,7 +7355,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.39.0(jiti@1.21.6) + eslint: 9.39.1(jiti@1.21.6) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -7010,11 +7364,11 @@ snapshots: safe-regex-test: 1.0.3 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@4.6.2(eslint@9.39.0(jiti@1.21.6)): + eslint-plugin-react-hooks@4.6.2(eslint@9.39.1(jiti@1.21.6)): dependencies: - eslint: 9.39.0(jiti@1.21.6) + eslint: 9.39.1(jiti@1.21.6) - eslint-plugin-react@7.37.5(eslint@9.39.0(jiti@1.21.6)): + eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@1.21.6)): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -7022,7 +7376,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.2.1 - eslint: 9.39.0(jiti@1.21.6) + eslint: 9.39.1(jiti@1.21.6) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -7045,35 +7399,35 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.0(jiti@1.21.6): + eslint@9.39.1(jiti@1.21.6): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0(jiti@1.21.6)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.6)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.0 + '@eslint/js': 9.39.1 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 + '@types/estree': 1.0.6 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.3.7 escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 - esquery: 1.6.0 + esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - ignore: 5.3.2 + ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 @@ -7092,7 +7446,7 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 - esquery@1.6.0: + esquery@1.5.0: dependencies: estraverse: 5.3.0 @@ -7110,7 +7464,7 @@ snapshots: execa@4.1.0: dependencies: - cross-spawn: 7.0.6 + cross-spawn: 7.0.3 get-stream: 5.2.0 human-signals: 1.1.1 is-stream: 2.0.1 @@ -7122,7 +7476,7 @@ snapshots: execa@8.0.1: dependencies: - cross-spawn: 7.0.6 + cross-spawn: 7.0.3 get-stream: 8.0.1 human-signals: 5.0.0 is-stream: 3.0.0 @@ -7138,13 +7492,13 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-glob@3.3.3: + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.8 + micromatch: 4.0.7 fast-json-parse@1.0.3: {} @@ -7158,7 +7512,7 @@ snapshots: fastq@1.17.1: dependencies: - reusify: 1.1.0 + reusify: 1.0.4 fdir@6.5.0(picomatch@4.0.3): optionalDependencies: @@ -7176,9 +7530,10 @@ snapshots: dependencies: to-regex-range: 5.0.1 - find-babel-config@2.1.2: + find-babel-config@2.1.1: dependencies: json5: 2.2.3 + path-exists: 4.0.0 find-up@3.0.0: dependencies: @@ -7191,14 +7546,14 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.3.3 + flatted: 3.3.1 keyv: 4.5.4 flatbuffers@1.12.0: {} flatbuffers@22.10.26: {} - flatted@3.3.3: {} + flatted@3.3.1: {} for-each@0.3.3: dependencies: @@ -7208,9 +7563,9 @@ snapshots: dependencies: is-callable: 1.2.7 - foreground-child@3.3.1: + foreground-child@3.2.1: dependencies: - cross-spawn: 7.0.6 + cross-spawn: 7.0.3 signal-exit: 4.1.0 form-data-encoder@2.1.4: {} @@ -7220,7 +7575,7 @@ snapshots: fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 - jsonfile: 6.2.0 + jsonfile: 6.1.0 universalify: 2.0.1 fs.realpath@1.0.0: {} @@ -7252,7 +7607,14 @@ snapshots: get-caller-file@2.0.5: {} - get-east-asian-width@1.3.1: {} + get-east-asian-width@1.2.0: {} + + get-intrinsic@1.2.1: + dependencies: + function-bind: 1.1.2 + has: 1.0.3 + has-proto: 1.0.3 + has-symbols: 1.0.3 get-intrinsic@1.2.4: dependencies: @@ -7300,7 +7662,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-tsconfig@4.10.1: + get-tsconfig@4.13.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -7312,13 +7674,12 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.4.5: + glob@10.4.1: dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 + foreground-child: 3.2.1 + jackspeak: 3.4.0 + minimatch: 9.0.4 minipass: 7.1.2 - package-json-from-dist: 1.0.1 path-scurry: 1.11.1 glob@9.3.5: @@ -7328,9 +7689,11 @@ snapshots: minipass: 4.2.8 path-scurry: 1.11.1 + globals@11.12.0: {} + globals@14.0.0: {} - globals@15.15.0: {} + globals@15.10.0: {} globalthis@1.0.4: dependencies: @@ -7341,8 +7704,8 @@ snapshots: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 + fast-glob: 3.3.2 + ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 @@ -7376,22 +7739,22 @@ snapshots: graphemer@1.4.0: {} - graphql-tag@2.12.6(graphql@15.10.1): + graphql-tag@2.12.6(graphql@15.9.0): dependencies: - graphql: 15.10.1 - tslib: 2.8.1 + graphql: 15.9.0 + tslib: 2.6.3 - graphql@15.10.1: {} + graphql@15.9.0: {} has-bigints@1.0.2: {} - has-bigints@1.1.0: {} + has-flag@3.0.0: {} has-flag@4.0.0: {} has-property-descriptors@1.0.2: dependencies: - es-define-property: 1.0.1 + es-define-property: 1.0.0 has-proto@1.0.3: {} @@ -7405,19 +7768,23 @@ snapshots: has-tostringtag@1.0.0: dependencies: - has-symbols: 1.1.0 + has-symbols: 1.0.3 has-tostringtag@1.0.2: dependencies: - has-symbols: 1.1.0 + has-symbols: 1.0.3 + + has@1.0.3: + dependencies: + function-bind: 1.1.2 hasown@2.0.2: dependencies: function-bind: 1.1.2 - hast-util-to-jsx-runtime@2.3.6: + hast-util-to-jsx-runtime@2.3.2: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.6 '@types/hast': 3.0.4 '@types/unist': 3.0.3 comma-separated-tokens: 2.0.3 @@ -7425,13 +7792,13 @@ snapshots: estree-util-is-identifier-name: 3.0.0 hast-util-whitespace: 3.0.0 mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdx-jsx: 3.1.3 mdast-util-mdxjs-esm: 2.0.1 - property-information: 7.1.0 + property-information: 6.5.0 space-separated-tokens: 2.0.2 - style-to-js: 1.1.17 + style-to-object: 1.0.8 unist-util-position: 5.0.0 - vfile-message: 4.0.3 + vfile-message: 4.0.2 transitivePeerDependencies: - supports-color @@ -7441,7 +7808,7 @@ snapshots: highlight.js@10.7.3: {} - hls.js@1.6.11: {} + hls.js@1.5.17: {} hoist-non-react-statics@3.3.2: dependencies: @@ -7449,7 +7816,7 @@ snapshots: html-url-attributes@3.0.1: {} - http-cache-semantics@4.2.0: {} + http-cache-semantics@4.1.1: {} http2-client@1.3.5: {} @@ -7461,7 +7828,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.3 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -7469,13 +7836,13 @@ snapshots: human-signals@5.0.0: {} - husky@9.1.7: {} + husky@9.1.6: {} hyphenate-style-name@1.1.0: {} ieee754@1.2.1: {} - ignore@5.3.2: {} + ignore@5.3.1: {} ignore@7.0.5: {} @@ -7484,46 +7851,45 @@ snapshots: immer@10.1.1: optional: true - immutable@5.1.3: {} + immutable@4.3.6: {} - import-fresh@3.3.1: + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 imurmurhash@0.1.4: {} - indent-string@5.0.0: {} + indent-string@4.0.0: {} - ink@5.2.1(@types/react@18.3.24)(react@18.3.1): + ink@3.2.0(@types/react@18.3.11)(react@18.3.1): dependencies: - '@alcalzone/ansi-tokenize': 0.1.3 - ansi-escapes: 7.0.0 - ansi-styles: 6.2.1 - auto-bind: 5.0.1 - chalk: 5.6.0 - cli-boxes: 3.0.0 - cli-cursor: 4.0.0 - cli-truncate: 4.0.0 - code-excerpt: 4.0.0 - es-toolkit: 1.39.10 - indent-string: 5.0.0 - is-in-ci: 1.0.0 - patch-console: 2.0.0 + ansi-escapes: 4.3.2 + auto-bind: 4.0.0 + chalk: 4.1.2 + cli-boxes: 2.2.1 + cli-cursor: 3.1.0 + cli-truncate: 2.1.0 + code-excerpt: 3.0.0 + indent-string: 4.0.0 + is-ci: 2.0.0 + lodash: 4.17.21 + patch-console: 1.0.0 react: 18.3.1 - react-reconciler: 0.29.2(react@18.3.1) - scheduler: 0.23.2 + react-devtools-core: 4.28.5 + react-reconciler: 0.26.2(react@18.3.1) + scheduler: 0.20.2 signal-exit: 3.0.7 - slice-ansi: 7.1.0 + slice-ansi: 3.0.0 stack-utils: 2.0.6 - string-width: 7.2.0 - type-fest: 4.41.0 - widest-line: 5.0.0 - wrap-ansi: 9.0.0 - ws: 8.18.3 - yoga-layout: 3.2.1 + string-width: 4.2.3 + type-fest: 0.12.0 + widest-line: 3.1.0 + wrap-ansi: 6.2.0 + ws: 7.5.10 + yoga-layout-prebuilt: 1.10.0 optionalDependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.11 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -7534,7 +7900,7 @@ snapshots: dependencies: es-errors: 1.3.0 hasown: 2.0.2 - side-channel: 1.1.0 + side-channel: 1.0.6 internal-slot@1.1.0: dependencies: @@ -7544,7 +7910,7 @@ snapshots: intl-pluralrules@2.0.1: {} - ip-num@1.5.2: {} + ip-num@1.5.1: {} is-alphabetical@2.0.1: {} @@ -7569,13 +7935,9 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 - is-async-function@2.1.1: + is-async-function@2.0.0: dependencies: - async-function: 1.0.0 - call-bound: 1.0.4 - get-proto: 1.0.1 has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 is-bigint@1.0.4: dependencies: @@ -7583,7 +7945,7 @@ snapshots: is-bigint@1.1.0: dependencies: - has-bigints: 1.1.0 + has-bigints: 1.0.2 is-binary-path@2.1.0: dependencies: @@ -7591,7 +7953,7 @@ snapshots: is-boolean-object@1.1.2: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.2 has-tostringtag: 1.0.0 is-boolean-object@1.2.2: @@ -7601,10 +7963,22 @@ snapshots: is-bun-module@2.0.0: dependencies: - semver: 7.7.2 + semver: 7.7.3 is-callable@1.2.7: {} + is-ci@2.0.0: + dependencies: + ci-info: 2.0.0 + + is-core-module@2.13.1: + dependencies: + hasown: 2.0.2 + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -7634,6 +8008,10 @@ snapshots: is-extglob@2.1.1: {} + is-finalizationregistry@1.0.2: + dependencies: + call-bind: 1.0.8 + is-finalizationregistry@1.1.1: dependencies: call-bound: 1.0.4 @@ -7642,16 +8020,13 @@ snapshots: is-fullwidth-code-point@4.0.0: {} - is-fullwidth-code-point@5.1.0: + is-fullwidth-code-point@5.0.0: dependencies: - get-east-asian-width: 1.3.1 + get-east-asian-width: 1.2.0 - is-generator-function@1.1.0: + is-generator-function@1.0.10: dependencies: - call-bound: 1.0.4 - get-proto: 1.0.1 has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 is-glob@4.0.3: dependencies: @@ -7659,8 +8034,6 @@ snapshots: is-hexadecimal@2.0.1: {} - is-in-ci@1.0.0: {} - is-map@2.0.3: {} is-negative-zero@2.0.3: {} @@ -7717,7 +8090,7 @@ snapshots: is-symbol@1.0.4: dependencies: - has-symbols: 1.1.0 + has-symbols: 1.0.3 is-symbol@1.1.1: dependencies: @@ -7759,20 +8132,18 @@ snapshots: iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 - es-object-atoms: 1.1.1 + es-object-atoms: 1.0.0 get-intrinsic: 1.3.0 get-proto: 1.0.1 has-symbols: 1.1.0 set-function-name: 2.0.2 - its-fine@1.2.5(@types/react@18.3.24)(react@18.3.1): + its-fine@1.2.5(react@18.3.1): dependencies: - '@types/react-reconciler': 0.28.9(@types/react@18.3.24) + '@types/react-reconciler': 0.28.8 react: 18.3.1 - transitivePeerDependencies: - - '@types/react' - jackspeak@3.4.3: + jackspeak@3.4.0: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: @@ -7780,11 +8151,9 @@ snapshots: jiti@1.21.6: {} - jotai@2.13.1(@babel/core@7.28.4)(@babel/template@7.27.2)(@types/react@18.3.24)(react@18.3.1): + jotai@2.12.2(@types/react@18.3.11)(react@18.3.1): optionalDependencies: - '@babel/core': 7.28.4 - '@babel/template': 7.27.2 - '@types/react': 18.3.24 + '@types/react': 18.3.11 react: 18.3.1 js-tokens@4.0.0: {} @@ -7793,7 +8162,9 @@ snapshots: dependencies: argparse: 2.0.1 - jsesc@3.1.0: {} + jsesc@2.5.2: {} + + jsesc@3.0.2: {} json-buffer@3.0.1: {} @@ -7809,7 +8180,7 @@ snapshots: json5@2.2.3: {} - jsonfile@6.2.0: + jsonfile@6.1.0: dependencies: universalify: 2.0.1 optionalDependencies: @@ -7843,26 +8214,28 @@ snapshots: dependencies: immediate: 3.0.6 - lilconfig@3.1.3: {} + lilconfig@2.1.0: {} + + lilconfig@3.1.2: {} lines-and-columns@1.2.4: {} - lint-staged@15.5.2: + lint-staged@15.2.10: dependencies: - chalk: 5.6.0 - commander: 13.1.0 - debug: 4.4.1 + chalk: 5.3.0 + commander: 12.1.0 + debug: 4.3.7 execa: 8.0.1 - lilconfig: 3.1.3 - listr2: 8.3.3 + lilconfig: 3.1.2 + listr2: 8.2.5 micromatch: 4.0.8 pidtree: 0.6.0 string-argv: 0.3.2 - yaml: 2.8.1 + yaml: 2.5.1 transitivePeerDependencies: - supports-color - listr2@8.3.3: + listr2@8.2.5: dependencies: cli-truncate: 4.0.0 colorette: 2.0.20 @@ -7904,20 +8277,20 @@ snapshots: lowercase-keys@3.0.0: {} - lru-cache@10.4.3: {} + lru-cache@10.2.2: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - maath@0.10.8(@types/three@0.163.0)(three@0.163.0): + maath@0.10.7(@types/three@0.163.0)(three@0.163.0): dependencies: '@types/three': 0.163.0 three: 0.163.0 magic-string@0.30.8: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.4.15 make-error@1.3.6: {} @@ -8021,7 +8394,7 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-mdx-jsx@3.2.0: + mdast-util-mdx-jsx@3.1.3: dependencies: '@types/estree-jsx': 1.0.5 '@types/hast': 3.0.4 @@ -8031,10 +8404,10 @@ snapshots: devlop: 1.1.0 mdast-util-from-markdown: 2.0.2 mdast-util-to-markdown: 2.1.2 - parse-entities: 4.0.2 + parse-entities: 4.0.1 stringify-entities: 4.0.4 unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.3 + vfile-message: 4.0.2 transitivePeerDependencies: - supports-color @@ -8058,9 +8431,9 @@ snapshots: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.0 + '@ungap/structured-clone': 1.2.0 devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 + micromark-util-sanitize-uri: 2.0.0 trim-lines: 3.0.1 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 @@ -8109,7 +8482,7 @@ snapshots: micromark-util-resolve-all: 2.0.0 micromark-util-subtokenize: 2.0.1 micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.2 + micromark-util-types: 2.0.0 micromark-extension-gfm-autolink-literal@2.1.0: dependencies: @@ -8171,45 +8544,40 @@ snapshots: micromark-factory-destination@2.0.0: dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 micromark-factory-label@2.0.0: dependencies: devlop: 1.1.0 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 micromark-factory-space@2.0.0: dependencies: micromark-util-character: 2.1.0 - micromark-util-types: 2.0.2 + micromark-util-types: 2.0.0 micromark-factory-title@2.0.0: dependencies: micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 micromark-factory-whitespace@2.0.0: dependencies: micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 micromark-util-character@2.1.0: dependencies: micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.2 - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 + micromark-util-types: 2.0.0 micromark-util-chunked@2.0.0: dependencies: @@ -8219,7 +8587,7 @@ snapshots: dependencies: micromark-util-character: 2.1.0 micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.2 + micromark-util-types: 2.0.0 micromark-util-combine-extensions@2.0.0: dependencies: @@ -8239,8 +8607,6 @@ snapshots: micromark-util-encode@2.0.0: {} - micromark-util-encode@2.0.1: {} - micromark-util-html-tag-name@2.0.0: {} micromark-util-normalize-identifier@2.0.0: @@ -8249,7 +8615,7 @@ snapshots: micromark-util-resolve-all@2.0.0: dependencies: - micromark-util-types: 2.0.2 + micromark-util-types: 2.0.0 micromark-util-sanitize-uri@2.0.0: dependencies: @@ -8257,31 +8623,21 @@ snapshots: micromark-util-encode: 2.0.0 micromark-util-symbol: 2.0.0 - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-subtokenize@2.0.1: dependencies: devlop: 1.1.0 micromark-util-chunked: 2.0.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 micromark-util-symbol@2.0.0: {} - micromark-util-symbol@2.0.1: {} - micromark-util-types@2.0.0: {} - micromark-util-types@2.0.2: {} - micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3 + debug: 4.3.7 decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.1 @@ -8293,10 +8649,10 @@ snapshots: micromark-util-encode: 2.0.0 micromark-util-normalize-identifier: 2.0.0 micromark-util-resolve-all: 2.0.0 - micromark-util-sanitize-uri: 2.0.1 + micromark-util-sanitize-uri: 2.0.0 micromark-util-subtokenize: 2.0.1 micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.2 + micromark-util-types: 2.0.0 transitivePeerDependencies: - supports-color @@ -8304,7 +8660,6 @@ snapshots: dependencies: braces: 3.0.3 picomatch: 2.3.1 - optional: true micromatch@4.0.8: dependencies: @@ -8329,11 +8684,11 @@ snapshots: minimatch@8.0.4: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.0.1 - minimatch@9.0.5: + minimatch@9.0.4: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.0.1 minimist@1.2.8: {} @@ -8341,6 +8696,8 @@ snapshots: minipass@7.1.2: {} + ms@2.1.2: {} + ms@2.1.3: {} mz@2.7.0: @@ -8349,14 +8706,13 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.11: {} + nanoid@3.3.7: {} - napi-postinstall@0.3.3: {} + napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} - node-addon-api@7.1.1: - optional: true + node-addon-api@7.1.1: {} node-fetch-h2@2.3.0: dependencies: @@ -8370,13 +8726,15 @@ snapshots: dependencies: es6-promise: 3.3.1 - node-releases@2.0.19: {} + node-releases@2.0.14: {} + + node-releases@2.0.18: {} normalize-path@3.0.0: {} normalize-range@0.1.2: {} - normalize-url@8.0.2: {} + normalize-url@8.0.1: {} npm-run-path@4.0.1: dependencies: @@ -8485,7 +8843,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.0.0 objectorarray@1.0.5: {} @@ -8515,12 +8873,12 @@ snapshots: dependencies: yaml: 1.10.2 - optimism@0.18.1: + optimism@0.18.0: dependencies: '@wry/caches': 1.0.1 '@wry/context': 0.7.4 - '@wry/trie': 0.5.0 - tslib: 2.8.1 + '@wry/trie': 0.4.3 + tslib: 2.6.3 optionator@0.9.4: dependencies: @@ -8557,18 +8915,17 @@ snapshots: p-try@2.2.0: {} - package-json-from-dist@1.0.1: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 - parse-entities@4.0.2: + parse-entities@4.0.1: dependencies: '@types/unist': 2.0.11 + character-entities: 2.0.2 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.0.2 is-alphanumerical: 2.0.1 is-decimal: 2.0.1 is-hexadecimal: 2.0.1 @@ -8581,7 +8938,7 @@ snapshots: parse5@6.0.1: {} - patch-console@2.0.0: {} + patch-console@1.0.0: {} path-exists@3.0.0: {} @@ -8595,13 +8952,15 @@ snapshots: path-scurry@1.11.1: dependencies: - lru-cache: 10.4.3 + lru-cache: 10.2.2 minipass: 7.1.2 path-type@4.0.0: {} pegjs@0.10.0: {} + picocolors@1.0.1: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -8612,7 +8971,7 @@ snapshots: pify@2.3.0: {} - pirates@4.0.7: {} + pirates@4.0.6: {} pkg-up@3.1.0: dependencies: @@ -8622,48 +8981,52 @@ snapshots: possible-typed-array-names@1.0.0: {} - possible-typed-array-names@1.1.0: {} - - postcss-import@15.1.0(postcss@8.5.6): + postcss-import@15.1.0(postcss@8.4.38): dependencies: - postcss: 8.5.6 + postcss: 8.4.38 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.10 + resolve: 1.22.8 - postcss-js@4.0.1(postcss@8.5.6): + postcss-js@4.0.1(postcss@8.4.38): dependencies: camelcase-css: 2.0.1 - postcss: 8.5.6 + postcss: 8.4.38 - postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@9.1.1(typescript@5.9.2)): + postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@9.1.1(typescript@5.6.3)): dependencies: - lilconfig: 3.1.3 - yaml: 2.8.1 + lilconfig: 3.1.2 + yaml: 2.4.5 optionalDependencies: - postcss: 8.5.6 - ts-node: 9.1.1(typescript@5.9.2) + postcss: 8.4.38 + ts-node: 9.1.1(typescript@5.6.3) - postcss-nested@6.2.0(postcss@8.5.6): + postcss-nested@6.0.1(postcss@8.4.38): dependencies: - postcss: 8.5.6 - postcss-selector-parser: 6.1.2 + postcss: 8.4.38 + postcss-selector-parser: 6.1.0 postcss-selector-parser@6.0.10: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@6.1.2: + postcss-selector-parser@6.1.0: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 postcss-value-parser@4.2.0: {} - postcss@8.5.6: + postcss@8.4.38: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + + postcss@8.4.47: + dependencies: + nanoid: 3.3.7 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -8671,7 +9034,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.6.2: {} + prettier@3.3.3: {} progress@2.0.3: {} @@ -8693,7 +9056,7 @@ snapshots: property-expr@2.0.6: {} - property-information@7.1.0: {} + property-information@6.5.0: {} proxy-from-env@1.1.0: {} @@ -8713,15 +9076,23 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 + react-devtools-core@4.28.5: + dependencies: + shell-quote: 1.8.1 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 react: 18.3.1 scheduler: 0.23.2 - react-error-boundary@4.1.2(react@18.3.1): + react-error-boundary@4.0.13(react@18.3.1): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.24.7 react: 18.3.1 react-fast-compare@3.2.2: {} @@ -8734,7 +9105,7 @@ snapshots: react-fast-compare: 3.2.2 react-side-effect: 2.1.2(react@18.3.1) - react-hook-form@7.65.0(react@18.3.1): + react-hook-form@7.66.0(react@18.3.1): dependencies: react: 18.3.1 @@ -8742,25 +9113,24 @@ snapshots: react-lifecycles-compat@3.0.4: {} - react-markdown@9.1.0(@types/react@18.3.24)(react@18.3.1): + react-markdown@9.0.1(@types/react@18.3.11)(react@18.3.1): dependencies: '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@types/react': 18.3.24 + '@types/react': 18.3.11 devlop: 1.1.0 - hast-util-to-jsx-runtime: 2.3.6 + hast-util-to-jsx-runtime: 2.3.2 html-url-attributes: 3.0.1 mdast-util-to-hast: 13.2.0 react: 18.3.1 remark-parse: 11.0.0 - remark-rehype: 11.1.2 + remark-rehype: 11.1.1 unified: 11.0.5 unist-util-visit: 5.0.0 vfile: 6.0.3 transitivePeerDependencies: - supports-color - react-modal@3.16.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-modal@3.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: exenv: 1.2.2 prop-types: 15.8.1 @@ -8769,21 +9139,22 @@ snapshots: react-lifecycles-compat: 3.0.4 warning: 4.0.3 + react-reconciler@0.26.2(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react: 18.3.1 + scheduler: 0.20.2 + react-reconciler@0.27.0(react@18.3.1): dependencies: loose-envify: 1.4.0 react: 18.3.1 scheduler: 0.21.0 - react-reconciler@0.29.2(react@18.3.1): - dependencies: - loose-envify: 1.4.0 - react: 18.3.1 - scheduler: 0.23.2 + react-refresh@0.14.2: {} - react-refresh@0.17.0: {} - - react-responsive@10.0.1(react@18.3.1): + react-responsive@10.0.0(react@18.3.1): dependencies: hyphenate-style-name: 1.1.0 matchmediaquery: 0.4.2 @@ -8791,28 +9162,22 @@ snapshots: react: 18.3.1 shallow-equal: 3.1.0 - react-router-dom@6.30.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-router-dom@6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@remix-run/router': 1.23.0 + '@remix-run/router': 1.20.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-router: 6.30.1(react@18.3.1) + react-router: 6.27.0(react@18.3.1) - react-router@6.30.1(react@18.3.1): + react-router@6.27.0(react@18.3.1): dependencies: - '@remix-run/router': 1.23.0 + '@remix-run/router': 1.20.0 react: 18.3.1 react-side-effect@2.1.2(react@18.3.1): dependencies: react: 18.3.1 - react-use-measure@2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - react: 18.3.1 - optionalDependencies: - react-dom: 18.3.1(react@18.3.1) - react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -8838,8 +9203,20 @@ snapshots: get-proto: 1.0.1 which-builtin-type: 1.2.1 + reflect.getprototypeof@1.0.6: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + which-builtin-type: 1.1.3 + reftools@1.1.9: {} + regenerator-runtime@0.14.1: {} + regexp.prototype.flags@1.5.2: dependencies: call-bind: 1.0.7 @@ -8856,12 +9233,12 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 - rehackt@0.1.0(@types/react@18.3.24)(react@18.3.1): + rehackt@0.1.0(@types/react@18.3.11)(react@18.3.1): optionalDependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.11 react: 18.3.1 - remark-gfm@4.0.1: + remark-gfm@4.0.0: dependencies: '@types/mdast': 4.0.4 mdast-util-gfm: 3.0.0 @@ -8876,12 +9253,12 @@ snapshots: dependencies: '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.2 - micromark-util-types: 2.0.2 + micromark-util-types: 2.0.0 unified: 11.0.5 transitivePeerDependencies: - supports-color - remark-rehype@11.1.2: + remark-rehype@11.1.1: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 @@ -8907,29 +9284,25 @@ snapshots: resolve-pkg-maps@1.0.0: {} - resolve@1.22.10: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - resolve@1.22.8: dependencies: - is-core-module: 2.16.1 + is-core-module: 2.13.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 resolve@2.0.0-next.5: dependencies: - is-core-module: 2.16.1 + is-core-module: 2.15.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + response-iterator@0.2.6: {} + responselike@3.0.0: dependencies: lowercase-keys: 3.0.0 - restore-cursor@4.0.0: + restore-cursor@3.1.0: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 @@ -8939,53 +9312,48 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - reusify@1.1.0: {} + reusify@1.0.4: {} rfdc@1.4.1: {} - rollup-plugin-visualizer@5.14.0(rollup@4.50.0): + rollup-plugin-visualizer@5.12.0(rollup@4.24.0): dependencies: open: 8.4.2 - picomatch: 4.0.3 - source-map: 0.7.6 + picomatch: 2.3.1 + source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.50.0 + rollup: 4.24.0 - rollup@4.50.0: + rollup@4.24.0: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.50.0 - '@rollup/rollup-android-arm64': 4.50.0 - '@rollup/rollup-darwin-arm64': 4.50.0 - '@rollup/rollup-darwin-x64': 4.50.0 - '@rollup/rollup-freebsd-arm64': 4.50.0 - '@rollup/rollup-freebsd-x64': 4.50.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.50.0 - '@rollup/rollup-linux-arm-musleabihf': 4.50.0 - '@rollup/rollup-linux-arm64-gnu': 4.50.0 - '@rollup/rollup-linux-arm64-musl': 4.50.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.50.0 - '@rollup/rollup-linux-ppc64-gnu': 4.50.0 - '@rollup/rollup-linux-riscv64-gnu': 4.50.0 - '@rollup/rollup-linux-riscv64-musl': 4.50.0 - '@rollup/rollup-linux-s390x-gnu': 4.50.0 - '@rollup/rollup-linux-x64-gnu': 4.50.0 - '@rollup/rollup-linux-x64-musl': 4.50.0 - '@rollup/rollup-openharmony-arm64': 4.50.0 - '@rollup/rollup-win32-arm64-msvc': 4.50.0 - '@rollup/rollup-win32-ia32-msvc': 4.50.0 - '@rollup/rollup-win32-x64-msvc': 4.50.0 + '@rollup/rollup-android-arm-eabi': 4.24.0 + '@rollup/rollup-android-arm64': 4.24.0 + '@rollup/rollup-darwin-arm64': 4.24.0 + '@rollup/rollup-darwin-x64': 4.24.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.24.0 + '@rollup/rollup-linux-arm-musleabihf': 4.24.0 + '@rollup/rollup-linux-arm64-gnu': 4.24.0 + '@rollup/rollup-linux-arm64-musl': 4.24.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.24.0 + '@rollup/rollup-linux-riscv64-gnu': 4.24.0 + '@rollup/rollup-linux-s390x-gnu': 4.24.0 + '@rollup/rollup-linux-x64-gnu': 4.24.0 + '@rollup/rollup-linux-x64-musl': 4.24.0 + '@rollup/rollup-win32-arm64-msvc': 4.24.0 + '@rollup/rollup-win32-ia32-msvc': 4.24.0 + '@rollup/rollup-win32-x64-msvc': 4.24.0 fsevents: 2.3.3 run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - rxjs@7.8.2: + rxjs@7.8.1: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 safe-array-concat@1.1.2: dependencies: @@ -9019,13 +9387,17 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 - sass@1.92.0: + sass@1.80.2: dependencies: - chokidar: 4.0.1 - immutable: 5.1.3 - source-map-js: 1.2.0 - optionalDependencies: '@parcel/watcher': 2.4.1 + chokidar: 4.0.1 + immutable: 4.3.6 + source-map-js: 1.2.0 + + scheduler@0.20.2: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 scheduler@0.21.0: dependencies: @@ -9037,7 +9409,9 @@ snapshots: semver@6.3.1: {} - semver@7.7.2: {} + semver@7.6.3: {} + + semver@7.7.3: {} set-function-length@1.2.2: dependencies: @@ -9045,7 +9419,7 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.2.4 - gopd: 1.2.0 + gopd: 1.0.1 has-property-descriptors: 1.0.2 set-function-name@2.0.2: @@ -9069,6 +9443,8 @@ snapshots: shebang-regex@3.0.0: {} + shell-quote@1.8.1: {} + should-equal@2.0.0: dependencies: should-type: 1.4.0 @@ -9140,6 +9516,12 @@ snapshots: slash@4.0.0: {} + slice-ansi@3.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + slice-ansi@5.0.0: dependencies: ansi-styles: 6.2.1 @@ -9148,7 +9530,7 @@ snapshots: slice-ansi@7.1.0: dependencies: ansi-styles: 6.2.1 - is-fullwidth-code-point: 5.1.0 + is-fullwidth-code-point: 5.0.0 source-map-js@1.2.0: {} @@ -9161,7 +9543,7 @@ snapshots: source-map@0.6.1: {} - source-map@0.7.6: {} + source-map@0.7.4: {} space-separated-tokens@2.0.2: {} @@ -9176,9 +9558,9 @@ snapshots: spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.22 + spdx-license-ids: 3.0.18 - spdx-license-ids@3.0.22: {} + spdx-license-ids@3.0.18: {} spdx-ranges@2.1.1: {} @@ -9194,16 +9576,15 @@ snapshots: dependencies: escape-string-regexp: 2.0.0 - stats-gl@2.4.2(@types/three@0.163.0)(three@0.163.0): + stats-gl@2.2.8: dependencies: '@types/three': 0.163.0 - three: 0.163.0 stats.js@0.17.0: {} stop-iteration-iterator@1.0.0: dependencies: - internal-slot: 1.1.0 + internal-slot: 1.0.7 stop-iteration-iterator@1.1.0: dependencies: @@ -9224,10 +9605,10 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - string-width@7.2.0: + string-width@7.1.0: dependencies: - emoji-regex: 10.5.0 - get-east-asian-width: 1.3.1 + emoji-regex: 10.3.0 + get-east-asian-width: 1.2.0 strip-ansi: 7.1.0 string.prototype.includes@2.0.1: @@ -9243,7 +9624,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.24.0 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.0.0 get-intrinsic: 1.3.0 gopd: 1.2.0 has-symbols: 1.1.0 @@ -9279,13 +9660,13 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.0.0 string.prototype.trimstart@1.0.8: dependencies: - call-bind: 1.0.7 + call-bind: 1.0.8 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 stringify-entities@4.0.4: dependencies: @@ -9298,7 +9679,7 @@ snapshots: strip-ansi@7.1.0: dependencies: - ansi-regex: 6.2.0 + ansi-regex: 6.0.1 strip-bom@3.0.0: {} @@ -9308,24 +9689,24 @@ snapshots: strip-json-comments@3.1.1: {} - style-to-js@1.1.17: - dependencies: - style-to-object: 1.0.9 - - style-to-object@1.0.9: + style-to-object@1.0.8: dependencies: inline-style-parser: 0.2.4 sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/gen-mapping': 0.3.5 commander: 4.1.1 - glob: 10.4.5 + glob: 10.4.1 lines-and-columns: 1.2.4 mz: 2.7.0 - pirates: 4.0.7 + pirates: 4.0.6 ts-interface-checker: 0.1.13 + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -9356,36 +9737,36 @@ snapshots: tailwind-gradient-mask-image@1.2.0: {} - tailwindcss@3.4.17(ts-node@9.1.1(typescript@5.9.2)): + tailwindcss@3.4.14(ts-node@9.1.1(typescript@5.6.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 chokidar: 3.6.0 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.3.3 + fast-glob: 3.3.2 glob-parent: 6.0.2 is-glob: 4.0.3 jiti: 1.21.6 - lilconfig: 3.1.3 - micromatch: 4.0.8 + lilconfig: 2.1.0 + micromatch: 4.0.7 normalize-path: 3.0.0 object-hash: 3.0.0 - picocolors: 1.1.1 - postcss: 8.5.6 - postcss-import: 15.1.0(postcss@8.5.6) - postcss-js: 4.0.1(postcss@8.5.6) - postcss-load-config: 4.0.2(postcss@8.5.6)(ts-node@9.1.1(typescript@5.9.2)) - postcss-nested: 6.2.0(postcss@8.5.6) - postcss-selector-parser: 6.1.2 - resolve: 1.22.10 + picocolors: 1.0.1 + postcss: 8.4.38 + postcss-import: 15.1.0(postcss@8.4.38) + postcss-js: 4.0.1(postcss@8.4.38) + postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@9.1.1(typescript@5.6.3)) + postcss-nested: 6.0.1(postcss@8.4.38) + postcss-selector-parser: 6.1.0 + resolve: 1.22.8 sucrase: 3.35.0 transitivePeerDependencies: - ts-node terser@5.31.1: dependencies: - '@jridgewell/source-map': 0.3.11 + '@jridgewell/source-map': 0.3.6 acorn: 8.15.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -9403,11 +9784,11 @@ snapshots: dependencies: three: 0.163.0 - three-stdlib@2.36.0(three@0.163.0): + three-stdlib@2.30.3(three@0.163.0): dependencies: '@types/draco3d': 1.4.10 '@types/offscreencanvas': 2019.7.3 - '@types/webxr': 0.5.23 + '@types/webxr': 0.5.16 draco3d: 1.5.7 fflate: 0.6.10 potpack: 1.0.2 @@ -9419,11 +9800,13 @@ snapshots: tinycolor2@1.6.0: {} - tinyglobby@0.2.14: + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + to-fast-properties@2.0.0: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -9434,35 +9817,35 @@ snapshots: trim-lines@3.0.1: {} - troika-three-text@0.52.4(three@0.163.0): + troika-three-text@0.49.1(three@0.163.0): dependencies: bidi-js: 1.0.3 three: 0.163.0 - troika-three-utils: 0.52.4(three@0.163.0) - troika-worker-utils: 0.52.0 + troika-three-utils: 0.49.0(three@0.163.0) + troika-worker-utils: 0.49.0 webgl-sdf-generator: 1.1.1 - troika-three-utils@0.52.4(three@0.163.0): + troika-three-utils@0.49.0(three@0.163.0): dependencies: three: 0.163.0 - troika-worker-utils@0.52.0: {} + troika-worker-utils@0.49.0: {} trough@2.2.0: {} - ts-api-utils@1.4.3(typescript@5.9.2): + ts-api-utils@1.3.0(typescript@5.6.3): dependencies: - typescript: 5.9.2 + typescript: 5.6.3 - ts-api-utils@2.1.0(typescript@5.9.2): + ts-api-utils@2.1.0(typescript@5.6.3): dependencies: - typescript: 5.9.2 + typescript: 5.6.3 ts-interface-checker@0.1.13: {} ts-invariant@0.10.3: dependencies: - tslib: 2.8.1 + tslib: 2.6.3 ts-node@9.1.1(typescript@4.8.4): dependencies: @@ -9474,18 +9857,18 @@ snapshots: typescript: 4.8.4 yn: 3.1.1 - ts-node@9.1.1(typescript@5.9.2): + ts-node@9.1.1(typescript@5.6.3): dependencies: arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 source-map-support: 0.5.21 - typescript: 5.9.2 + typescript: 5.6.3 yn: 3.1.1 optional: true - ts-pattern@5.8.0: {} + ts-pattern@5.5.0: {} tsconfig-paths@3.15.0: dependencies: @@ -9496,16 +9879,16 @@ snapshots: tslib@1.14.1: {} - tslib@2.8.1: {} + tslib@2.6.3: {} tsutils@3.21.0(typescript@4.8.2): dependencies: tslib: 1.14.1 typescript: 4.8.2 - tunnel-rat@0.1.2(@types/react@18.3.24)(immer@10.1.1)(react@18.3.1): + tunnel-rat@0.1.2(@types/react@18.3.11)(immer@10.1.1)(react@18.3.1): dependencies: - zustand: 4.5.7(@types/react@18.3.24)(immer@10.1.1)(react@18.3.1) + zustand: 4.5.2(@types/react@18.3.11)(immer@10.1.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' - immer @@ -9517,9 +9900,11 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-fest@2.19.0: {} + type-fest@0.12.0: {} - type-fest@4.41.0: {} + type-fest@0.21.3: {} + + type-fest@2.19.0: {} typed-array-buffer@1.0.2: dependencies: @@ -9544,7 +9929,7 @@ snapshots: typed-array-byte-length@1.0.3: dependencies: call-bind: 1.0.8 - for-each: 0.3.5 + for-each: 0.3.3 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 @@ -9562,7 +9947,7 @@ snapshots: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.8 - for-each: 0.3.5 + for-each: 0.3.3 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 @@ -9580,20 +9965,20 @@ snapshots: typed-array-length@1.0.7: dependencies: call-bind: 1.0.8 - for-each: 0.3.5 + for-each: 0.3.3 gopd: 1.2.0 is-typed-array: 1.1.15 - possible-typed-array-names: 1.1.0 - reflect.getprototypeof: 1.0.10 + possible-typed-array-names: 1.0.0 + reflect.getprototypeof: 1.0.6 - typescript-eslint@8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2): + typescript-eslint@8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2))(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) - '@typescript-eslint/parser': 8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.2) - '@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@1.21.6))(typescript@5.9.2) - eslint: 9.39.0(jiti@1.21.6) - typescript: 5.9.2 + '@typescript-eslint/eslint-plugin': 8.46.4(@typescript-eslint/parser@8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3))(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/parser': 8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/typescript-estree': 8.46.4(typescript@5.6.3) + '@typescript-eslint/utils': 8.46.4(eslint@9.39.1(jiti@1.21.6))(typescript@5.6.3) + eslint: 9.39.1(jiti@1.21.6) + typescript: 5.6.3 transitivePeerDependencies: - supports-color @@ -9601,7 +9986,7 @@ snapshots: typescript@4.8.4: {} - typescript@5.9.2: {} + typescript@5.6.3: {} unbox-primitive@1.0.2: dependencies: @@ -9613,11 +9998,11 @@ snapshots: unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 - has-bigints: 1.1.0 + has-bigints: 1.0.2 has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - undici-types@7.10.0: {} + undici-types@7.16.0: {} unified@11.0.5: dependencies: @@ -9656,14 +10041,14 @@ snapshots: unplugin@1.0.1: dependencies: - acorn: 8.15.0 + acorn: 8.12.0 chokidar: 3.6.0 - webpack-sources: 3.3.3 + webpack-sources: 3.2.3 webpack-virtual-modules: 0.5.0 unrs-resolver@1.11.1: dependencies: - napi-postinstall: 0.3.3 + napi-postinstall: 0.3.4 optionalDependencies: '@unrs/resolver-binding-android-arm-eabi': 1.11.1 '@unrs/resolver-binding-android-arm64': 1.11.1 @@ -9685,9 +10070,15 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.1.3(browserslist@4.25.4): + update-browserslist-db@1.0.16(browserslist@4.23.1): dependencies: - browserslist: 4.25.4 + browserslist: 4.23.1 + escalade: 3.1.2 + picocolors: 1.0.1 + + update-browserslist-db@1.1.1(browserslist@4.24.0): + dependencies: + browserslist: 4.24.0 escalade: 3.2.0 picocolors: 1.1.1 @@ -9695,11 +10086,11 @@ snapshots: dependencies: punycode: 2.3.1 - use-double-tap@1.3.7(react@18.3.1): + use-double-tap@1.3.6(react@18.3.1): dependencies: react: 18.3.1 - use-sync-external-store@1.5.0(react@18.3.1): + use-sync-external-store@1.2.0(react@18.3.1): dependencies: react: 18.3.1 @@ -9707,7 +10098,9 @@ snapshots: utility-types@3.11.0: {} - vfile-message@4.0.3: + uuid@9.0.1: {} + + vfile-message@4.0.2: dependencies: '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 @@ -9715,17 +10108,17 @@ snapshots: vfile@6.0.3: dependencies: '@types/unist': 3.0.3 - vfile-message: 4.0.3 + vfile-message: 4.0.2 - vite@5.4.19(@types/node@24.3.1)(sass@1.92.0)(terser@5.31.1): + vite@5.4.9(@types/node@24.10.0)(sass@1.80.2)(terser@5.31.1): dependencies: esbuild: 0.21.5 - postcss: 8.5.6 - rollup: 4.50.0 + postcss: 8.4.47 + rollup: 4.24.0 optionalDependencies: - '@types/node': 24.3.1 + '@types/node': 24.10.0 fsevents: 2.3.3 - sass: 1.92.0 + sass: 1.80.2 terser: 5.31.1 warning@4.0.3: @@ -9738,7 +10131,7 @@ snapshots: webidl-conversions@3.0.1: {} - webpack-sources@3.3.3: {} + webpack-sources@3.2.3: {} webpack-virtual-modules@0.5.0: {} @@ -9763,15 +10156,30 @@ snapshots: is-string: 1.1.1 is-symbol: 1.1.1 + which-builtin-type@1.1.3: + dependencies: + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.0.0 + is-date-object: 1.0.5 + is-finalizationregistry: 1.0.2 + is-generator-function: 1.0.10 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + which-builtin-type@1.2.1: dependencies: call-bound: 1.0.4 function.prototype.name: 1.1.8 has-tostringtag: 1.0.2 - is-async-function: 2.1.1 + is-async-function: 2.0.0 is-date-object: 1.1.0 is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.0 + is-generator-function: 1.0.10 is-regex: 1.2.1 is-weakref: 1.1.1 isarray: 2.0.5 @@ -9808,12 +10216,18 @@ snapshots: dependencies: isexe: 2.0.0 - widest-line@5.0.0: + widest-line@3.1.0: dependencies: - string-width: 7.2.0 + string-width: 4.2.3 word-wrap@1.2.5: {} + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -9829,12 +10243,12 @@ snapshots: wrap-ansi@9.0.0: dependencies: ansi-styles: 6.2.1 - string-width: 7.2.0 + string-width: 7.1.0 strip-ansi: 7.1.0 wrappy@1.0.2: {} - ws@8.18.3: {} + ws@7.5.10: {} y18n@5.0.8: {} @@ -9842,7 +10256,9 @@ snapshots: yaml@1.10.2: {} - yaml@2.8.1: {} + yaml@2.4.5: {} + + yaml@2.5.1: {} yargs-parser@20.2.9: {} @@ -9861,7 +10277,7 @@ snapshots: yargs@17.7.2: dependencies: cliui: 8.0.1 - escalade: 3.2.0 + escalade: 3.1.2 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 @@ -9872,9 +10288,11 @@ snapshots: yocto-queue@0.1.0: {} - yoga-layout@3.2.1: {} + yoga-layout-prebuilt@1.10.0: + dependencies: + '@types/yoga-layout': 1.9.2 - yup@1.7.0: + yup@1.4.0: dependencies: property-expr: 2.0.6 tiny-case: 1.0.3 @@ -9891,19 +10309,12 @@ snapshots: optionalDependencies: react: 18.3.1 - zustand@4.5.7(@types/react@18.3.24)(immer@10.1.1)(react@18.3.1): + zustand@4.5.2(@types/react@18.3.11)(immer@10.1.1)(react@18.3.1): dependencies: - use-sync-external-store: 1.5.0(react@18.3.1) + use-sync-external-store: 1.2.0(react@18.3.1) optionalDependencies: - '@types/react': 18.3.24 + '@types/react': 18.3.11 immer: 10.1.1 react: 18.3.1 - zustand@5.0.8(@types/react@18.3.24)(immer@10.1.1)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)): - optionalDependencies: - '@types/react': 18.3.24 - immer: 10.1.1 - react: 18.3.1 - use-sync-external-store: 1.5.0(react@18.3.1) - zwitch@2.0.4: {} diff --git a/server/core/src/main/java/dev/slimevr/NetworkProfileChecker.kt b/server/core/src/main/java/dev/slimevr/NetworkProfileChecker.kt new file mode 100644 index 000000000..4c6e77b0a --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/NetworkProfileChecker.kt @@ -0,0 +1,59 @@ +package dev.slimevr + +data class NetworkInfo( + val name: String?, + val description: String?, + val category: NetworkCategory?, + val connectivity: Set?, + val connected: Boolean?, +) + +/** + * @see NLM_NETWORK_CATEGORY enumeration (netlistmgr.h) + */ +enum class NetworkCategory(val value: Int) { + PUBLIC(0), + PRIVATE(1), + DOMAIN_AUTHENTICATED(2), + ; + + companion object { + fun fromInt(value: Int) = values().find { it.value == value } + } +} + +/** + * @see NLM_CONNECTIVITY enumeration (netlistmgr.h) + */ +enum class ConnectivityFlags(val value: Int) { + DISCONNECTED(0), + IPV4_NOTRAFFIC(0x1), + IPV6_NOTRAFFIC(0x2), + IPV4_SUBNET(0x10), + IPV4_LOCALNETWORK(0x20), + IPV4_INTERNET(0x40), + IPV6_SUBNET(0x100), + IPV6_LOCALNETWORK(0x200), + IPV6_INTERNET(0x400), + ; + + companion object { + fun fromInt(value: Int): Set = if (value == 0) { + setOf(DISCONNECTED) + } else { + values().filter { it != DISCONNECTED && (value and it.value) != 0 }.toSet() + } + } +} + +abstract class NetworkProfileChecker { + abstract val isSupported: Boolean + abstract val publicNetworks: List +} + +class NetworkProfileCheckerStub : NetworkProfileChecker() { + override val isSupported: Boolean + get() = false + override val publicNetworks: List + get() = listOf() +} diff --git a/server/core/src/main/java/dev/slimevr/VRServer.kt b/server/core/src/main/java/dev/slimevr/VRServer.kt index 519ebf9e6..0112b4ccc 100644 --- a/server/core/src/main/java/dev/slimevr/VRServer.kt +++ b/server/core/src/main/java/dev/slimevr/VRServer.kt @@ -18,6 +18,8 @@ import dev.slimevr.posestreamer.BVHRecorder import dev.slimevr.protocol.ProtocolAPI import dev.slimevr.protocol.rpc.settings.RPCSettingsHandler import dev.slimevr.reset.ResetHandler +import dev.slimevr.reset.ResetTimerManager +import dev.slimevr.reset.resetTimer import dev.slimevr.serial.ProvisioningHandler import dev.slimevr.serial.SerialHandler import dev.slimevr.serial.SerialHandlerStub @@ -28,6 +30,7 @@ import dev.slimevr.tracking.processor.HumanPoseManager import dev.slimevr.tracking.processor.skeleton.HumanSkeleton import dev.slimevr.tracking.trackers.* import dev.slimevr.tracking.trackers.udp.TrackersUDPServer +import dev.slimevr.trackingchecklist.TrackingChecklistManager import dev.slimevr.util.ann.VRServerThread import dev.slimevr.websocketapi.WebSocketVRBridge import io.eiren.util.ann.ThreadSafe @@ -55,6 +58,7 @@ class VRServer @JvmOverloads constructor( serialHandlerProvider: (VRServer) -> SerialHandler = { _ -> SerialHandlerStub() }, flashingHandlerProvider: (VRServer) -> SerialFlashingHandler? = { _ -> null }, vrcConfigHandlerProvider: (VRServer) -> VRCConfigHandler = { _ -> VRCConfigHandlerStub() }, + networkProfileProvider: (VRServer) -> NetworkProfileChecker = { _ -> NetworkProfileCheckerStub() }, acquireMulticastLock: () -> Any? = { null }, @JvmField val configManager: ConfigManager, ) : Thread("VRServer") { @@ -99,6 +103,7 @@ class VRServer @JvmOverloads constructor( @JvmField val protocolAPI: ProtocolAPI private val timer = Timer() + private val resetTimerManager = ResetTimerManager() val fpsTimer = NanoTimer() @JvmField @@ -113,6 +118,10 @@ class VRServer @JvmOverloads constructor( @JvmField val handshakeHandler = HandshakeHandler() + val trackingChecklistManager: TrackingChecklistManager + + val networkProfileChecker: NetworkProfileChecker + init { // UwU deviceManager = DeviceManager(this) @@ -126,6 +135,8 @@ class VRServer @JvmOverloads constructor( autoBoneHandler = AutoBoneHandler(this) firmwareUpdateHandler = FirmwareUpdateHandler(this) vrcConfigManager = VRChatConfigManager(this, vrcConfigHandlerProvider(this)) + networkProfileChecker = networkProfileProvider(this) + trackingChecklistManager = TrackingChecklistManager(this) protocolAPI = ProtocolAPI(this) val computedTrackers = humanPoseManager.computedTrackers @@ -164,6 +175,7 @@ class VRServer @JvmOverloads constructor( for (tracker in computedTrackers) { registerTracker(tracker) } + instance = this } @@ -300,7 +312,7 @@ class VRServer @JvmOverloads constructor( queueTask { humanPoseManager.resetTrackersYaw(resetSourceName, bodyParts) } } - fun resetTrackersMounting(resetSourceName: String?, bodyParts: List = TrackerUtils.allBodyPartsButFingers) { + fun resetTrackersMounting(resetSourceName: String?, bodyParts: List? = null) { queueTask { humanPoseManager.resetTrackersMounting(resetSourceName, bodyParts) } } @@ -330,40 +342,52 @@ class VRServer @JvmOverloads constructor( } } - fun scheduleResetTrackersFull(resetSourceName: String?, delay: Long) { - if (delay > 0) { - resetHandler.sendStarted(ResetType.Full) - } - timer.schedule(delay) { - queueTask { - humanPoseManager.resetTrackersFull(resetSourceName) - resetHandler.sendFinished(ResetType.Full) - } - } + fun scheduleResetTrackersFull(resetSourceName: String?, delay: Long, bodyParts: List = ArrayList()) { + resetTimer( + resetTimerManager, + delay, + onTick = { progress -> + resetHandler.sendStarted(ResetType.Full, bodyParts, progress, delay.toInt()) + }, + onComplete = { + queueTask { + humanPoseManager.resetTrackersFull(resetSourceName, bodyParts) + resetHandler.sendFinished(ResetType.Full, bodyParts, delay.toInt()) + } + }, + ) } - fun scheduleResetTrackersYaw(resetSourceName: String?, delay: Long) { - if (delay > 0) { - resetHandler.sendStarted(ResetType.Yaw) - } - timer.schedule(delay) { - queueTask { - humanPoseManager.resetTrackersYaw(resetSourceName) - resetHandler.sendFinished(ResetType.Yaw) - } - } + fun scheduleResetTrackersYaw(resetSourceName: String?, delay: Long, bodyParts: List = TrackerUtils.allBodyPartsButFingers) { + resetTimer( + resetTimerManager, + delay, + onTick = { progress -> + resetHandler.sendStarted(ResetType.Yaw, bodyParts, progress, delay.toInt()) + }, + onComplete = { + queueTask { + humanPoseManager.resetTrackersYaw(resetSourceName, bodyParts) + resetHandler.sendFinished(ResetType.Yaw, bodyParts, delay.toInt()) + } + }, + ) } - fun scheduleResetTrackersMounting(resetSourceName: String?, delay: Long) { - if (delay > 0) { - resetHandler.sendStarted(ResetType.Mounting) - } - timer.schedule(delay) { - queueTask { - humanPoseManager.resetTrackersMounting(resetSourceName) - resetHandler.sendFinished(ResetType.Mounting) - } - } + fun scheduleResetTrackersMounting(resetSourceName: String?, delay: Long, bodyParts: List? = null) { + resetTimer( + resetTimerManager, + delay, + onTick = { progress -> + resetHandler.sendStarted(ResetType.Mounting, bodyParts, progress, delay.toInt()) + }, + onComplete = { + queueTask { + humanPoseManager.resetTrackersMounting(resetSourceName, bodyParts) + resetHandler.sendFinished(ResetType.Mounting, bodyParts, delay.toInt()) + } + }, + ) } fun scheduleSetPauseTracking(pauseTracking: Boolean, sourceName: String?, delay: Long) { diff --git a/server/core/src/main/java/dev/slimevr/bridge/Bridge.kt b/server/core/src/main/java/dev/slimevr/bridge/Bridge.kt index ca869e367..5750b53b3 100644 --- a/server/core/src/main/java/dev/slimevr/bridge/Bridge.kt +++ b/server/core/src/main/java/dev/slimevr/bridge/Bridge.kt @@ -53,4 +53,6 @@ interface ISteamVRBridge : Bridge { fun getAutomaticSharedTrackers(): Boolean fun setAutomaticSharedTrackers(value: Boolean) + + fun getBridgeConfigKey(): String } diff --git a/server/core/src/main/java/dev/slimevr/config/ResetsConfig.kt b/server/core/src/main/java/dev/slimevr/config/ResetsConfig.kt index ce33bddd8..65cc761d8 100644 --- a/server/core/src/main/java/dev/slimevr/config/ResetsConfig.kt +++ b/server/core/src/main/java/dev/slimevr/config/ResetsConfig.kt @@ -29,6 +29,24 @@ enum class ArmsResetModes(val id: Int) { } } +enum class MountingMethods(val id: Int) { + MANUAL(0), + AUTOMATIC(1), + ; + + companion object { + val values = MountingMethods.entries.toTypedArray() + + @JvmStatic + fun fromId(id: Int): MountingMethods? { + for (filter in values) { + if (filter.id == id) return filter + } + return null + } + } +} + class ResetsConfig { // Always reset mounting for feet @@ -46,6 +64,12 @@ class ResetsConfig { // Reset the HMD's pitch upon full reset var resetHmdPitch = false + var lastMountingMethod = MountingMethods.AUTOMATIC + + var yawResetDelay = 0.0f + var fullResetDelay = 3.0f + var mountingResetDelay = 3.0f + fun updateTrackersResetsSettings() { for (t in VRServer.instance.allTrackers) { t.resetsHandler.readResetConfig(this) diff --git a/server/core/src/main/java/dev/slimevr/config/TapDetectionConfig.kt b/server/core/src/main/java/dev/slimevr/config/TapDetectionConfig.kt index 721addd0b..0e5a83cde 100644 --- a/server/core/src/main/java/dev/slimevr/config/TapDetectionConfig.kt +++ b/server/core/src/main/java/dev/slimevr/config/TapDetectionConfig.kt @@ -14,20 +14,16 @@ class TapDetectionConfig { var mountingResetEnabled = true var setupMode = false var yawResetTaps = 2 - // clamp to 2-3 to prevent errors set(yawResetTaps) { - field = FastMath.clamp(yawResetTaps.toFloat(), 2f, 10f).toInt() - field = yawResetTaps + field = yawResetTaps.coerceIn(2, 10) } var fullResetTaps = 3 set(fullResetTaps) { - field = FastMath.clamp(fullResetTaps.toFloat(), 2f, 10f).toInt() - field = fullResetTaps + field = fullResetTaps.coerceIn(2, 10) } var mountingResetTaps = 3 set(mountingResetTaps) { - field = FastMath.clamp(mountingResetTaps.toFloat(), 2f, 10f).toInt() - field = mountingResetTaps + field = mountingResetTaps.coerceIn(2, 10) } var numberTrackersOverThreshold = 1 } diff --git a/server/core/src/main/java/dev/slimevr/config/TrackingChecklistConfig.kt b/server/core/src/main/java/dev/slimevr/config/TrackingChecklistConfig.kt new file mode 100644 index 000000000..182a97bff --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/config/TrackingChecklistConfig.kt @@ -0,0 +1,5 @@ +package dev.slimevr.config + +class TrackingChecklistConfig { + val ignoredStepsIds: MutableList = mutableListOf() +} diff --git a/server/core/src/main/java/dev/slimevr/config/VRCConfig.kt b/server/core/src/main/java/dev/slimevr/config/VRCConfig.kt new file mode 100644 index 000000000..fd40b13dd --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/config/VRCConfig.kt @@ -0,0 +1,6 @@ +package dev.slimevr.config + +class VRCConfig { + // List of fields ignored in vrc warnings - @see VRCConfigValidity + val mutedWarnings: MutableList = mutableListOf() +} diff --git a/server/core/src/main/java/dev/slimevr/config/VRConfig.kt b/server/core/src/main/java/dev/slimevr/config/VRConfig.kt index de6bc9319..0a01a4d42 100644 --- a/server/core/src/main/java/dev/slimevr/config/VRConfig.kt +++ b/server/core/src/main/java/dev/slimevr/config/VRConfig.kt @@ -54,6 +54,10 @@ class VRConfig { val overlay: OverlayConfig = OverlayConfig() + val trackingChecklist: TrackingChecklistConfig = TrackingChecklistConfig() + + val vrcConfig: VRCConfig = VRCConfig() + init { // Initialize default settings for OSC Router oscRouter.portIn = 9002 @@ -104,7 +108,7 @@ class VRConfig { tracker.readConfig(config) if (tracker.isImu()) tracker.resetsHandler.readDriftCompensationConfig(driftCompensation) tracker.resetsHandler.readResetConfig(resetsConfig) - if (tracker.needsReset) { + if (tracker.allowReset) { tracker.saveMountingResetOrientation(config) } if (tracker.allowFiltering) { diff --git a/server/core/src/main/java/dev/slimevr/games/vrchat/VRCConfigHandler.kt b/server/core/src/main/java/dev/slimevr/games/vrchat/VRCConfigHandler.kt index 8e54d5de8..fc2829e80 100644 --- a/server/core/src/main/java/dev/slimevr/games/vrchat/VRCConfigHandler.kt +++ b/server/core/src/main/java/dev/slimevr/games/vrchat/VRCConfigHandler.kt @@ -78,11 +78,11 @@ data class VRCConfigValidity( val shoulderTrackingOk: Boolean, val shoulderWidthCompensationOk: Boolean, val userHeightOk: Boolean, - val calibrationOk: Boolean, + val calibrationRangeOk: Boolean, val calibrationVisualsOk: Boolean, - val tackerModelOk: Boolean, + val trackerModelOk: Boolean, val spineModeOk: Boolean, - val avatarMeasurementOk: Boolean, + val avatarMeasurementTypeOk: Boolean, ) abstract class VRCConfigHandler { @@ -98,13 +98,14 @@ class VRCConfigHandlerStub : VRCConfigHandler() { } interface VRCConfigListener { - fun onChange(validity: VRCConfigValidity, values: VRCConfigValues, recommended: VRCConfigRecommendedValues) + fun onChange(validity: VRCConfigValidity, values: VRCConfigValues, recommended: VRCConfigRecommendedValues, muted: List) } class VRChatConfigManager(val server: VRServer, private val handler: VRCConfigHandler) { private val listeners: MutableList = CopyOnWriteArrayList() var currentValues: VRCConfigValues? = null + var currentValidity: VRCConfigValidity? = null val isSupported: Boolean get() = handler.isSupported @@ -113,6 +114,31 @@ class VRChatConfigManager(val server: VRServer, private val handler: VRCConfigHa handler.initHandler(::onChange) } + fun toggleMuteWarning(key: String) { + val keys = VRCConfigValidity::class.java.declaredFields.asSequence().map { p -> p.name } + if (!keys.contains(key)) return + + if (!server.configManager.vrConfig.vrcConfig.mutedWarnings.contains(key)) { + server.configManager.vrConfig.vrcConfig.mutedWarnings.add(key) + } else { + server.configManager.vrConfig.vrcConfig.mutedWarnings.remove(key) + } + + server.configManager.saveConfig() + + val recommended = recommendedValues() + val validity = currentValidity ?: return + val values = currentValues ?: return + listeners.forEach { + it.onChange( + validity, + values, + recommended, + server.configManager.vrConfig.vrcConfig.mutedWarnings, + ) + } + } + /** * shoulderTrackingDisabled should be true if: * The user isn't tracking their whole arms from their controllers: @@ -160,20 +186,28 @@ class VRChatConfigManager(val server: VRServer, private val handler: VRCConfigHa legacyModeOk = values.legacyMode == recommended.legacyMode, shoulderTrackingOk = values.shoulderTrackingDisabled == recommended.shoulderTrackingDisabled, spineModeOk = recommended.spineMode.contains(values.spineMode), - tackerModelOk = values.trackerModel == recommended.trackerModel, - calibrationOk = abs(values.calibrationRange - recommended.calibrationRange) < 0.1, + trackerModelOk = values.trackerModel == recommended.trackerModel, + calibrationRangeOk = abs(values.calibrationRange - recommended.calibrationRange) < 0.1, userHeightOk = abs(server.humanPoseManager.realUserHeight - values.userHeight) < 0.1, calibrationVisualsOk = values.calibrationVisuals == recommended.calibrationVisuals, - avatarMeasurementOk = values.avatarMeasurementType == recommended.avatarMeasurementType, + avatarMeasurementTypeOk = values.avatarMeasurementType == recommended.avatarMeasurementType, shoulderWidthCompensationOk = values.shoulderWidthCompensation == recommended.shoulderWidthCompensation, ) + fun forceUpdate() { + val values = currentValues + if (values != null) { + this.onChange(values) + } + } + fun onChange(values: VRCConfigValues) { val recommended = recommendedValues() val validity = checkValidity(values, recommended) + currentValidity = validity currentValues = values listeners.forEach { - it.onChange(validity, values, recommended) + it.onChange(validity, values, recommended, server.configManager.vrConfig.vrcConfig.mutedWarnings) } } } diff --git a/server/core/src/main/java/dev/slimevr/osc/VMCHandler.kt b/server/core/src/main/java/dev/slimevr/osc/VMCHandler.kt index 775f305e7..52b40325e 100644 --- a/server/core/src/main/java/dev/slimevr/osc/VMCHandler.kt +++ b/server/core/src/main/java/dev/slimevr/osc/VMCHandler.kt @@ -290,7 +290,7 @@ class VMCHandler( userEditable = true, isComputed = position != null, usesTimeout = true, - needsReset = position != null, + allowReset = position != null, ) trackerDevice!!.trackers[trackerDevice!!.trackers.size] = tracker byTrackerNameTracker[name] = tracker diff --git a/server/core/src/main/java/dev/slimevr/osc/VRCOSCHandler.kt b/server/core/src/main/java/dev/slimevr/osc/VRCOSCHandler.kt index abfbaa379..17c0459fd 100644 --- a/server/core/src/main/java/dev/slimevr/osc/VRCOSCHandler.kt +++ b/server/core/src/main/java/dev/slimevr/osc/VRCOSCHandler.kt @@ -275,7 +275,7 @@ class VRCOSCHandler( hasPosition = true, userEditable = true, isComputed = true, - needsReset = trackerPosition != TrackerPosition.HEAD, + allowReset = trackerPosition != TrackerPosition.HEAD, usesTimeout = true, ) vrsystemTrackersDevice!!.trackers[trackerPosition.ordinal] = tracker @@ -368,7 +368,7 @@ class VRCOSCHandler( hasPosition = true, userEditable = true, isComputed = true, - needsReset = true, + allowReset = true, usesTimeout = true, ) oscTrackersDevice!!.trackers[trackerId] = tracker diff --git a/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java b/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java index b2efbd0ab..3c256ff2f 100644 --- a/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java +++ b/server/core/src/main/java/dev/slimevr/protocol/datafeed/DataFeedBuilder.java @@ -141,7 +141,7 @@ public class DataFeedBuilder { TrackerInfo.addAllowDriftCompensation(fbb, false); } - if (tracker.getNeedsMounting()) { + if (tracker.getAllowMounting()) { Quaternion quaternion = tracker.getResetsHandler().getMountingOrientation(); Quaternion mountResetFix = tracker.getResetsHandler().getMountRotFix(); TrackerInfo.addMountingOrientation(fbb, createQuat(fbb, quaternion)); @@ -215,7 +215,7 @@ public class DataFeedBuilder { if (trackerTemperatureOffset != 0) TrackerData.addTemp(fbb, trackerTemperatureOffset); } - if (tracker.getNeedsMounting() && tracker.getHasRotation()) { + if (tracker.getAllowMounting() && tracker.getHasRotation()) { if (mask.getRotationReferenceAdjusted()) { TrackerData .addRotationReferenceAdjusted(fbb, createQuat(fbb, tracker.getRotation())); @@ -227,7 +227,7 @@ public class DataFeedBuilder { createQuat(fbb, tracker.getIdentityAdjustedRotation()) ); } - } else if (tracker.getNeedsReset() && tracker.getHasRotation()) { + } else if (tracker.getAllowReset() && tracker.getHasRotation()) { if (mask.getRotationReferenceAdjusted()) { TrackerData .addRotationReferenceAdjusted(fbb, createQuat(fbb, tracker.getRotation())); diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt b/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt index ffb90ab08..873befeaf 100644 --- a/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt +++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/RPCHandler.kt @@ -1,6 +1,7 @@ package dev.slimevr.protocol.rpc import com.google.flatbuffers.FlatBufferBuilder +import dev.slimevr.config.MountingMethods import dev.slimevr.config.config import dev.slimevr.protocol.GenericConnection import dev.slimevr.protocol.ProtocolAPI @@ -18,6 +19,7 @@ import dev.slimevr.protocol.rpc.setup.RPCHandshakeHandler import dev.slimevr.protocol.rpc.setup.RPCTapSetupHandler import dev.slimevr.protocol.rpc.setup.RPCUtil.getLocalIp import dev.slimevr.protocol.rpc.status.RPCStatusHandler +import dev.slimevr.protocol.rpc.trackingchecklist.RPCTrackingChecklistHandler import dev.slimevr.protocol.rpc.trackingpause.RPCTrackingPause import dev.slimevr.tracking.processor.config.SkeletonConfigOffsets import dev.slimevr.tracking.processor.stayaligned.poses.RelaxedPose @@ -48,184 +50,104 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler - this.onResetRequest( - conn, - messageHeader, - ) - } - registerPacketListener( - RpcMessage.ClearMountingResetRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onClearMountingResetRequest( - conn, - messageHeader, - ) - } registerPacketListener( RpcMessage.AssignTrackerRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onAssignTrackerRequest( - conn, - messageHeader, - ) - } + ::onAssignTrackerRequest, + ) registerPacketListener( RpcMessage.ClearDriftCompensationRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onClearDriftCompensationRequest( - conn, - messageHeader, - ) - } + ::onClearDriftCompensationRequest, + ) registerPacketListener( RpcMessage.RecordBVHRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onRecordBVHRequest( - conn, - messageHeader, - ) - } + ::onRecordBVHRequest, + ) registerPacketListener( RpcMessage.RecordBVHStatusRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onBVHStatusRequest( - conn, - messageHeader, - ) - } + ::onBVHStatusRequest, + ) registerPacketListener( RpcMessage.SkeletonResetAllRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onSkeletonResetAllRequest( - conn, - messageHeader, - ) - } + ::onSkeletonResetAllRequest, + ) registerPacketListener( RpcMessage.SkeletonConfigRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onSkeletonConfigRequest( - conn, - messageHeader, - ) - } + ::onSkeletonConfigRequest, + ) registerPacketListener( RpcMessage.ChangeSkeletonConfigRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onChangeSkeletonConfigRequest( - conn, - messageHeader, - ) - } + ::onChangeSkeletonConfigRequest, + ) registerPacketListener( RpcMessage.OverlayDisplayModeChangeRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onOverlayDisplayModeChangeRequest( - conn, - messageHeader, - ) - } + ::onOverlayDisplayModeChangeRequest, + ) registerPacketListener( RpcMessage.OverlayDisplayModeRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onOverlayDisplayModeRequest( - conn, - messageHeader, - ) - } + ::onOverlayDisplayModeRequest, + ) registerPacketListener( RpcMessage.ServerInfosRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onServerInfosRequest( - conn, - messageHeader, - ) - } + ::onServerInfosRequest, + ) registerPacketListener( RpcMessage.LegTweaksTmpChange, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onLegTweaksTmpChange( - conn, - messageHeader, - ) - } + ::onLegTweaksTmpChange, + ) registerPacketListener( RpcMessage.LegTweaksTmpClear, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onLegTweaksTmpClear( - conn, - messageHeader, - ) - } + ::onLegTweaksTmpClear, + ) registerPacketListener( RpcMessage.StatusSystemRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onStatusSystemRequest( - conn, - messageHeader, - ) - } + ::onStatusSystemRequest, + ) registerPacketListener( RpcMessage.SetPauseTrackingRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onSetPauseTrackingRequest( - conn, - messageHeader, - ) - } + ::onSetPauseTrackingRequest, + ) registerPacketListener( RpcMessage.HeightRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onHeightRequest( - conn, - messageHeader, - ) - } + ::onHeightRequest, + ) registerPacketListener( RpcMessage.MagToggleRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onMagToggleRequest(conn, messageHeader) - } + ::onMagToggleRequest, + ) registerPacketListener( RpcMessage.ChangeMagToggleRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onChangeMagToggleRequest(conn, messageHeader) - } + ::onChangeMagToggleRequest, + ) registerPacketListener( RpcMessage.EnableStayAlignedRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onEnableStayAlignedRequest(conn, messageHeader) - } + ::onEnableStayAlignedRequest, + ) registerPacketListener( RpcMessage.DetectStayAlignedRelaxedPoseRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onDetectStayAlignedRelaxedPoseRequest(conn, messageHeader) - } + ::onDetectStayAlignedRelaxedPoseRequest, + ) registerPacketListener( RpcMessage.ResetStayAlignedRelaxedPoseRequest, - ) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onResetStayAlignedRelaxedPoseRequest(conn, messageHeader) - } + ::onResetStayAlignedRelaxedPoseRequest, + ) } private fun onServerInfosRequest( @@ -347,55 +269,6 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler() - if (req.bodyPartsLength() > 0) { - val buffer = req.bodyPartsAsByteBuffer() - while (buffer.hasRemaining()) { - bodyParts.add(buffer.get().toInt()) - } - } - - if (req.resetType() == ResetType.Yaw) { - if (bodyParts.isEmpty()) { - api.server.resetTrackersYaw(RESET_SOURCE_NAME) - } else { - api.server.resetTrackersYaw(RESET_SOURCE_NAME, bodyParts.toList()) - } - } - if (req.resetType() == ResetType.Full) { - if (bodyParts.isEmpty()) { - api.server.resetTrackersFull(RESET_SOURCE_NAME) - } else { - api.server.resetTrackersFull(RESET_SOURCE_NAME, bodyParts.toList()) - } - } - if (req.resetType() == ResetType.Mounting) { - if (bodyParts.isEmpty()) { - api.server.resetTrackersMounting(RESET_SOURCE_NAME) - } else { - api.server.resetTrackersMounting(RESET_SOURCE_NAME, bodyParts.toList()) - } - } - } - - fun onClearMountingResetRequest( - conn: GenericConnection, - messageHeader: RpcMessageHeader, - ) { - if (messageHeader - .message(ClearMountingResetRequest()) !is ClearMountingResetRequest - ) { - return - } - - api.server.clearTrackersMounting(RESET_SOURCE_NAME) - } - fun onAssignTrackerRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) { val req = messageHeader .message(AssignTrackerRequest()) as? AssignTrackerRequest ?: return @@ -415,7 +288,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler, ): Int { if (!isSupported) { VRCConfigStateChangeResponse.startVRCConfigStateChangeResponse(fbb) @@ -71,11 +72,13 @@ fun buildVRCConfigStateResponse( val validityOffset = buildVRCConfigValidity(fbb, validity) val valuesOffset = buildVRCConfigValues(fbb, values) val recommendedOffset = buildVRCConfigRecommendedValues(fbb, recommended) + val mutedOffset = VRCConfigStateChangeResponse.createMutedVector(fbb, muted.map { fbb.createString(it) }.toIntArray()) VRCConfigStateChangeResponse.startVRCConfigStateChangeResponse(fbb) VRCConfigStateChangeResponse.addIsSupported(fbb, true) VRCConfigStateChangeResponse.addValidity(fbb, validityOffset) VRCConfigStateChangeResponse.addState(fbb, valuesOffset) VRCConfigStateChangeResponse.addRecommended(fbb, recommendedOffset) + VRCConfigStateChangeResponse.addMuted(fbb, mutedOffset) return VRCConfigStateChangeResponse.endVRCConfigStateChangeResponse(fbb) } diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/games/vrchat/RPCVRChatHandler.kt b/server/core/src/main/java/dev/slimevr/protocol/rpc/games/vrchat/RPCVRChatHandler.kt index 2d4bd9277..481e918d3 100644 --- a/server/core/src/main/java/dev/slimevr/protocol/rpc/games/vrchat/RPCVRChatHandler.kt +++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/games/vrchat/RPCVRChatHandler.kt @@ -18,12 +18,8 @@ class RPCVRChatHandler( init { api.server.vrcConfigManager.addListener(this) - rpcHandler.registerPacketListener(RpcMessage.VRCConfigStateRequest) { conn: GenericConnection, messageHeader: RpcMessageHeader -> - this.onConfigStateRequest( - conn, - messageHeader, - ) - } + rpcHandler.registerPacketListener(RpcMessage.VRCConfigStateRequest, ::onConfigStateRequest) + rpcHandler.registerPacketListener(RpcMessage.VRCConfigSettingToggleMute, ::onToggleMuteRequest) } private fun onConfigStateRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) { @@ -41,6 +37,7 @@ class RPCVRChatHandler( validity = validity, values = values, recommended = api.server.vrcConfigManager.recommendedValues(), + muted = api.server.configManager.vrConfig.vrcConfig.mutedWarnings, ) val outbound = rpcHandler.createRPCMessage( @@ -52,7 +49,13 @@ class RPCVRChatHandler( conn.send(fbb.dataBuffer()) } - override fun onChange(validity: VRCConfigValidity, values: VRCConfigValues, recommended: VRCConfigRecommendedValues) { + private fun onToggleMuteRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) { + val req = messageHeader.message(VRCConfigSettingToggleMute()) as VRCConfigSettingToggleMute? + ?: return + api.server.vrcConfigManager.toggleMuteWarning(req.key()) + } + + override fun onChange(validity: VRCConfigValidity, values: VRCConfigValues, recommended: VRCConfigRecommendedValues, muted: List) { val fbb = FlatBufferBuilder(32) val response = buildVRCConfigStateResponse( @@ -61,6 +64,7 @@ class RPCVRChatHandler( validity = validity, values = values, recommended = recommended, + muted, ) val outbound = rpcHandler.createRPCMessage( diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/reset/RPCResetHandler.java b/server/core/src/main/java/dev/slimevr/protocol/rpc/reset/RPCResetHandler.java deleted file mode 100644 index 9dcb7ffc9..000000000 --- a/server/core/src/main/java/dev/slimevr/protocol/rpc/reset/RPCResetHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -package dev.slimevr.protocol.rpc.reset; - -import java.util.function.Consumer; - -import com.google.flatbuffers.FlatBufferBuilder; - -import dev.slimevr.protocol.GenericConnection; - -import dev.slimevr.protocol.ProtocolAPI; -import dev.slimevr.protocol.rpc.RPCHandler; -import dev.slimevr.reset.ResetListener; -import solarxr_protocol.rpc.ResetResponse; -import solarxr_protocol.rpc.ResetStatus; -import solarxr_protocol.rpc.RpcMessage; - - -public class RPCResetHandler implements ResetListener { - public RPCHandler rpcHandler; - public ProtocolAPI api; - - public RPCResetHandler(RPCHandler rpcHandler, ProtocolAPI api) { - this.rpcHandler = rpcHandler; - this.api = api; - - this.api.server.resetHandler.addListener(this); - } - - public void sendResetStatusResponse(int resetType, int status) { - FlatBufferBuilder fbb = new FlatBufferBuilder(32); - ResetResponse.startResetResponse(fbb); - ResetResponse.addResetType(fbb, resetType); - ResetResponse.addStatus(fbb, status); - int update = ResetResponse.endResetResponse(fbb); - int outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.ResetResponse, update); - fbb.finish(outbound); - - this.forAllListeners((conn) -> { - conn.send(fbb.dataBuffer()); - }); - } - - @Override - public void onStarted(int resetType) { - sendResetStatusResponse(resetType, ResetStatus.STARTED); - } - - @Override - public void onFinished(int resetType) { - sendResetStatusResponse(resetType, ResetStatus.FINISHED); - } - - public void forAllListeners(Consumer action) { - this.api - .getAPIServers() - .forEach( - (server) -> server - .getAPIConnections() - .forEach(action) - ); - } -} - diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/reset/RPCResetHandler.kt b/server/core/src/main/java/dev/slimevr/protocol/rpc/reset/RPCResetHandler.kt new file mode 100644 index 000000000..fd9e598ae --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/reset/RPCResetHandler.kt @@ -0,0 +1,124 @@ +package dev.slimevr.protocol.rpc.reset + +import com.google.flatbuffers.FlatBufferBuilder +import dev.slimevr.protocol.GenericConnection +import dev.slimevr.protocol.ProtocolAPI +import dev.slimevr.protocol.ProtocolAPIServer +import dev.slimevr.protocol.rpc.RPCHandler +import dev.slimevr.reset.ResetListener +import solarxr_protocol.rpc.ClearMountingResetRequest +import solarxr_protocol.rpc.ResetRequest +import solarxr_protocol.rpc.ResetResponse +import solarxr_protocol.rpc.ResetStatus +import solarxr_protocol.rpc.ResetType +import solarxr_protocol.rpc.RpcMessage +import solarxr_protocol.rpc.RpcMessageHeader +import java.util.function.Consumer + +class RPCResetHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) : ResetListener { + val resetsConfig = api.server.configManager.vrConfig.resetsConfig + + init { + this.api.server.resetHandler.addListener(this) + + rpcHandler.registerPacketListener(RpcMessage.ResetRequest, ::onResetRequest) + rpcHandler.registerPacketListener(RpcMessage.ClearMountingResetRequest, ::onClearMountingResetRequest) + } + + fun onResetRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) { + val req = messageHeader.message(ResetRequest()) as? ResetRequest ?: return + + // Get the list of bodyparts we want to reset + // If empty, check in HumanSkeleton will reset all + val bodyParts = mutableListOf() + if (req.bodyPartsLength() > 0) { + val buffer = req.bodyPartsAsByteBuffer() + while (buffer.hasRemaining()) { + bodyParts.add(buffer.get().toInt()) + } + } + + if (req.resetType() == ResetType.Yaw) { + if (bodyParts.isEmpty()) { + api.server.scheduleResetTrackersYaw(RESET_SOURCE_NAME, (resetsConfig.yawResetDelay * 1000).toLong()) + } else { + api.server.scheduleResetTrackersYaw(RESET_SOURCE_NAME, (resetsConfig.yawResetDelay * 1000).toLong(), bodyParts.toList()) + } + } + if (req.resetType() == ResetType.Full) { + if (bodyParts.isEmpty()) { + api.server.scheduleResetTrackersFull(RESET_SOURCE_NAME, (resetsConfig.fullResetDelay * 1000).toLong()) + } else { + api.server.scheduleResetTrackersFull(RESET_SOURCE_NAME, (resetsConfig.fullResetDelay * 1000).toLong(), bodyParts.toList()) + } + } + if (req.resetType() == ResetType.Mounting) { + if (bodyParts.isEmpty()) { + api.server.scheduleResetTrackersMounting(RESET_SOURCE_NAME, (resetsConfig.mountingResetDelay * 1000).toLong()) + } else { + api.server.scheduleResetTrackersMounting(RESET_SOURCE_NAME, (resetsConfig.mountingResetDelay * 1000).toLong(), bodyParts.toList()) + } + } + } + + fun sendResetStatusResponse(resetType: Int, status: Int, bodyParts: List? = null, progress: Int = 0, duration: Int = 0) { + val fbb = FlatBufferBuilder(32) + + val bodyPartsOffset = if (bodyParts != null) ResetResponse.createBodyPartsVector(fbb, bodyParts.map { it.toByte() }.toByteArray()) else 0 + + ResetResponse.startResetResponse(fbb) + ResetResponse.addResetType(fbb, resetType) + ResetResponse.addStatus(fbb, status) + if (bodyPartsOffset >= 0) { + ResetResponse.addBodyParts(fbb, bodyPartsOffset) + } + ResetResponse.addProgress(fbb, progress) + ResetResponse.addDuration(fbb, duration) + val update = ResetResponse.endResetResponse(fbb) + val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.ResetResponse, update) + fbb.finish(outbound) + + this.forAllListeners( + Consumer { conn: GenericConnection -> + conn.send(fbb.dataBuffer()) + }, + ) + } + + fun onClearMountingResetRequest( + conn: GenericConnection, + messageHeader: RpcMessageHeader, + ) { + if (messageHeader + .message(ClearMountingResetRequest()) !is ClearMountingResetRequest + ) { + return + } + + api.server.clearTrackersMounting(RESET_SOURCE_NAME) + } + + override fun onStarted(resetType: Int, bodyParts: List?, progress: Int, duration: Int) { + sendResetStatusResponse(resetType, ResetStatus.STARTED, bodyParts, progress, duration) + } + + override fun onFinished(resetType: Int, bodyParts: List?, duration: Int) { + sendResetStatusResponse(resetType, ResetStatus.FINISHED, bodyParts, duration, duration) + } + + fun forAllListeners(action: Consumer?) { + this.api + .getAPIServers() + .forEach( + Consumer { server: ProtocolAPIServer? -> + server!! + .getAPIConnections() + .forEach(action) + }, + ) + } + + companion object { + const val RESET_SOURCE_NAME = "WebSocketAPI" + } +} diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/trackingchecklist/RPCTrackingChecklistHandler.kt b/server/core/src/main/java/dev/slimevr/protocol/rpc/trackingchecklist/RPCTrackingChecklistHandler.kt new file mode 100644 index 000000000..ca6f6b34e --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/trackingchecklist/RPCTrackingChecklistHandler.kt @@ -0,0 +1,73 @@ +package dev.slimevr.protocol.rpc.trackingchecklist + +import com.google.flatbuffers.FlatBufferBuilder +import dev.slimevr.protocol.GenericConnection +import dev.slimevr.protocol.ProtocolAPI +import dev.slimevr.protocol.rpc.RPCHandler +import dev.slimevr.trackingchecklist.TrackingChecklistListener +import solarxr_protocol.rpc.* + +class RPCTrackingChecklistHandler( + private val rpcHandler: RPCHandler, + var api: ProtocolAPI, +) : TrackingChecklistListener { + + init { + api.server.trackingChecklistManager.addListener(this) + + rpcHandler.registerPacketListener(RpcMessage.TrackingChecklistRequest, ::onTrackingChecklistRequest) + rpcHandler.registerPacketListener(RpcMessage.IgnoreTrackingChecklistStepRequest, ::onToggleTrackingChecklistRequest) + } + + fun buildTrackingChecklistResponse(fbb: FlatBufferBuilder): Int = TrackingChecklistResponse.pack( + fbb, + TrackingChecklistResponseT().apply { + steps = api.server.trackingChecklistManager.steps.toTypedArray() + ignoredSteps = api.server.configManager.vrConfig.trackingChecklist.ignoredStepsIds.toIntArray() + }, + ) + + private fun onTrackingChecklistRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) { + val fbb = FlatBufferBuilder(32) + val response = buildTrackingChecklistResponse(fbb) + val outbound = rpcHandler.createRPCMessage( + fbb, + RpcMessage.TrackingChecklistResponse, + response, + ) + fbb.finish(outbound) + conn.send(fbb.dataBuffer()) + } + + private fun onToggleTrackingChecklistRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) { + val req = messageHeader.message(IgnoreTrackingChecklistStepRequest()) as IgnoreTrackingChecklistStepRequest? + ?: return + val step = api.server.trackingChecklistManager.steps.find { it.id == req.stepId() } ?: error("invalid step id requested") + + api.server.trackingChecklistManager.ignoreStep(step, req.ignore()) + + val fbb = FlatBufferBuilder(32) + val response = buildTrackingChecklistResponse(fbb) + val outbound = rpcHandler.createRPCMessage( + fbb, + RpcMessage.TrackingChecklistResponse, + response, + ) + fbb.finish(outbound) + conn.send(fbb.dataBuffer()) + } + + override fun onStepsUpdate() { + val fbb = FlatBufferBuilder(32) + val response = buildTrackingChecklistResponse(fbb) + val outbound = rpcHandler.createRPCMessage( + fbb, + RpcMessage.TrackingChecklistResponse, + response, + ) + fbb.finish(outbound) + this.api.apiServers.forEach { apiServer -> + apiServer.apiConnections.forEach { it.send(fbb.dataBuffer()) } + } + } +} diff --git a/server/core/src/main/java/dev/slimevr/reset/ResetHandler.java b/server/core/src/main/java/dev/slimevr/reset/ResetHandler.java deleted file mode 100644 index ef28c1090..000000000 --- a/server/core/src/main/java/dev/slimevr/reset/ResetHandler.java +++ /dev/null @@ -1,30 +0,0 @@ -package dev.slimevr.reset; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - - -public class ResetHandler { - - private final List listeners = new CopyOnWriteArrayList<>(); - - public ResetHandler() { - - } - - public void sendStarted(int resetType) { - this.listeners.forEach((listener) -> listener.onStarted(resetType)); - } - - public void sendFinished(int resetType) { - this.listeners.forEach((listener) -> listener.onFinished(resetType)); - } - - public void addListener(ResetListener listener) { - this.listeners.add(listener); - } - - public void removeListener(ResetListener l) { - listeners.removeIf(listener -> l == listener); - } -} diff --git a/server/core/src/main/java/dev/slimevr/reset/ResetHandler.kt b/server/core/src/main/java/dev/slimevr/reset/ResetHandler.kt new file mode 100644 index 000000000..69a68c6cb --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/reset/ResetHandler.kt @@ -0,0 +1,24 @@ +package dev.slimevr.reset + +import java.util.concurrent.CopyOnWriteArrayList +import java.util.function.Consumer + +class ResetHandler { + private val listeners: MutableList = CopyOnWriteArrayList() + + fun sendStarted(resetType: Int, bodyParts: List? = null, progress: Int = 0, duration: Int = 0) { + this.listeners.forEach { listener: ResetListener -> listener.onStarted(resetType, bodyParts, progress, duration) } + } + + fun sendFinished(resetType: Int, bodyParts: List? = null, duration: Int) { + this.listeners.forEach { listener: ResetListener -> listener.onFinished(resetType, bodyParts, duration) } + } + + fun addListener(listener: ResetListener) { + this.listeners.add(listener) + } + + fun removeListener(l: ResetListener) { + listeners.removeIf { listener: ResetListener -> l === listener } + } +} diff --git a/server/core/src/main/java/dev/slimevr/reset/ResetListener.java b/server/core/src/main/java/dev/slimevr/reset/ResetListener.java deleted file mode 100644 index de3b8ea95..000000000 --- a/server/core/src/main/java/dev/slimevr/reset/ResetListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package dev.slimevr.reset; - -public interface ResetListener { - - void onStarted(int resetType); - - void onFinished(int resetType); -} diff --git a/server/core/src/main/java/dev/slimevr/reset/ResetListener.kt b/server/core/src/main/java/dev/slimevr/reset/ResetListener.kt new file mode 100644 index 000000000..b50fd3a91 --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/reset/ResetListener.kt @@ -0,0 +1,7 @@ +package dev.slimevr.reset + +interface ResetListener { + fun onStarted(resetType: Int, bodyParts: List? = null, progress: Int, duration: Int) + + fun onFinished(resetType: Int, bodyParts: List? = null, duration: Int) +} diff --git a/server/core/src/main/java/dev/slimevr/reset/ResetTimer.kt b/server/core/src/main/java/dev/slimevr/reset/ResetTimer.kt new file mode 100644 index 000000000..2734e2af3 --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/reset/ResetTimer.kt @@ -0,0 +1,40 @@ +package dev.slimevr.reset + +import java.util.Timer +import java.util.TimerTask +import kotlin.concurrent.schedule +import kotlin.math.floor +import kotlin.math.min + +class ResetTimerManager { + val timer: Timer = Timer() + val timers: ArrayList = arrayListOf() + + fun cancelTimers() { + timers.forEach { it.cancel() } + } +} + +fun resetTimer(resetTimerManager: ResetTimerManager, delay: Long, onTick: (progress: Int) -> Unit, onComplete: () -> Unit) { + resetTimerManager.cancelTimers() + + if (delay == 0L) { + onComplete() + return + } + + val ticks: Int = floor(delay / 1000f).toInt() + for (tick in 0..ticks) { + if (tick * 1000L == delay) continue + resetTimerManager.timers.add( + resetTimerManager.timer.schedule(tick * 1000L) { + onTick(tick * 1000) + }, + ) + } + resetTimerManager.timers.add( + resetTimerManager.timer.schedule(delay) { + onComplete() + }, + ) +} diff --git a/server/core/src/main/java/dev/slimevr/status/StatusSystem.kt b/server/core/src/main/java/dev/slimevr/status/StatusSystem.kt index fe590b6be..bd521f9a8 100644 --- a/server/core/src/main/java/dev/slimevr/status/StatusSystem.kt +++ b/server/core/src/main/java/dev/slimevr/status/StatusSystem.kt @@ -28,36 +28,6 @@ class StatusSystem { status }.toTypedArray() - /** - * @return the ID of the status, 0 is not a valid ID, can be used as replacement of null - */ - @JvmName("addStatusInt") - fun addStatus(statusData: StatusDataUnion, prioritized: Boolean = false): UInt { - val id = idCounter.getAndUpdate { - (it.toUInt() + 1u).toInt() // the simple way of making unsigned math - } - statuses[id] = statusData - if (prioritized) { - prioritizedStatuses.add(id) - } - - listeners.forEach { - it.onStatusChanged(id.toUInt(), statusData, prioritized) - } - - return id.toUInt() - } - - @JvmName("removeStatusInt") - fun removeStatus(id: UInt) { - statuses.remove(id.toInt()) - prioritizedStatuses.remove(id.toInt()) - - listeners.forEach { - it.onStatusRemoved(id) - } - } - fun hasStatusType(dataType: Byte): Boolean = statuses.any { it.value.type == dataType } diff --git a/server/core/src/main/java/dev/slimevr/tracking/processor/HumanPoseManager.kt b/server/core/src/main/java/dev/slimevr/tracking/processor/HumanPoseManager.kt index 6d7324249..f1fa12269 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/processor/HumanPoseManager.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/processor/HumanPoseManager.kt @@ -20,11 +20,6 @@ import io.github.axisangles.ktmath.Quaternion.Companion.IDENTITY import io.github.axisangles.ktmath.Vector3 import io.github.axisangles.ktmath.Vector3.Companion.POS_Y import org.apache.commons.math3.util.Precision -import solarxr_protocol.datatypes.DeviceIdT -import solarxr_protocol.datatypes.TrackerIdT -import solarxr_protocol.rpc.StatusData -import solarxr_protocol.rpc.StatusDataUnion -import solarxr_protocol.rpc.StatusUnassignedHMDT import java.util.function.Consumer import kotlin.math.* @@ -526,7 +521,7 @@ class HumanPoseManager(val server: VRServer?) { for (tracker in server!!.allTrackers) { if (( tracker.isImu() && - tracker.needsReset + tracker.allowReset ) && tracker.resetsHandler.lastResetQuaternion != null ) { @@ -574,8 +569,15 @@ class HumanPoseManager(val server: VRServer?) { } @JvmOverloads - fun resetTrackersMounting(resetSourceName: String?, bodyParts: List = TrackerUtils.allBodyPartsButFingers) { - skeleton.resetTrackersMounting(resetSourceName, bodyParts) + fun resetTrackersMounting(resetSourceName: String?, bodyParts: List? = null) { + val finalBodyParts = bodyParts + ?: if (server?.configManager?.vrConfig?.resetsConfig?.resetMountingFeet == true) { + TrackerUtils.allBodyPartsButFingers + } else { + TrackerUtils.allBodyPartsButFingersAndFeets + } + + skeleton.resetTrackersMounting(resetSourceName, finalBodyParts) } fun clearTrackersMounting(resetSourceName: String?) { @@ -677,51 +679,12 @@ class HumanPoseManager(val server: VRServer?) { return } server.allTrackers - .filter { !it.isInternal && it.trackerPosition != null } + .filter { it.trackerPosition != null } .forEach { - it.checkReportRequireReset() - } - } - - private var lastMissingHmdStatus = 0u - fun checkReportMissingHmd() { - // Check if this is main skeleton, there is no head tracker currently, - // and there is an available HMD one - if (server == null) return - val tracker = VRServer.instance.allTrackers.firstOrNull { it.isHmd && !it.isInternal && it.status.sendData } - if (skeleton.headTracker == null && - lastMissingHmdStatus == 0u && - tracker != null - ) { - reportMissingHmd(tracker) - } else if (lastMissingHmdStatus != 0u && - (skeleton.headTracker != null || tracker == null) - ) { - server.statusSystem.removeStatus(lastMissingHmdStatus) - lastMissingHmdStatus = 0u - } - } - - private fun reportMissingHmd(tracker: Tracker) { - require(lastMissingHmdStatus == 0u) { - "${::lastMissingHmdStatus.name} must be 0u, but was $lastMissingHmdStatus" - } - require(server != null) { - "${::server.name} must not be null" - } - - val status = StatusDataUnion().apply { - type = StatusData.StatusUnassignedHMD - value = StatusUnassignedHMDT().apply { - trackerId = TrackerIdT().apply { - if (tracker.device != null) { - deviceId = DeviceIdT().apply { id = tracker.device.id } - } - trackerNum = tracker.trackerNum + if (it.allowReset && !it.needReset) { + it.needReset = true } } - } - lastMissingHmdStatus = server.statusSystem.addStatus(status, true) } // #endregion diff --git a/server/core/src/main/java/dev/slimevr/tracking/processor/config/SkeletonConfigManager.kt b/server/core/src/main/java/dev/slimevr/tracking/processor/config/SkeletonConfigManager.kt index 62adbaec4..7cdec98b7 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/processor/config/SkeletonConfigManager.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/processor/config/SkeletonConfigManager.kt @@ -91,6 +91,9 @@ class SkeletonConfigManager( // Re-calculate user height userHeightFromOffsets = calculateUserHeight() userNeckHeightFromOffsets = userHeightFromOffsets - getOffset(SkeletonConfigOffsets.NECK) + + // Update vrc config checker if user height change + humanPoseManager?.server?.vrcConfigManager?.forceUpdate() } fun setOffset(config: SkeletonConfigOffsets, newValue: Float?) { diff --git a/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/HumanSkeleton.kt b/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/HumanSkeleton.kt index 9edbbc0e6..865b417bf 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/HumanSkeleton.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/HumanSkeleton.kt @@ -1,6 +1,7 @@ package dev.slimevr.tracking.processor.skeleton import dev.slimevr.VRServer +import dev.slimevr.config.MountingMethods import dev.slimevr.config.StayAlignedConfig import dev.slimevr.tracking.processor.Bone import dev.slimevr.tracking.processor.BoneType @@ -124,7 +125,6 @@ class HumanSkeleton( var headTracker: Tracker? by Delegates.observable(null) { _, old, new -> if (old == new) return@observable - humanPoseManager.checkReportMissingHmd() humanPoseManager.checkTrackersRequiringReset() } var neckTracker: Tracker? = null @@ -212,7 +212,7 @@ class HumanSkeleton( // Modules var legTweaks = LegTweaks(this) - var tapDetectionManager = TapDetectionManager(this) + var tapDetectionManager: TapDetectionManager? = null var localizer = Localizer(this) var ikSolver = IKSolver(headBone) @@ -232,12 +232,9 @@ class HumanSkeleton( ) : this(humanPoseManager) { setTrackersFromList(server.allTrackers) tapDetectionManager = TapDetectionManager( + server, this, humanPoseManager, - server.configManager.vrConfig.tapDetection, - server.resetHandler, - server.tapSetupHandler, - server.allTrackers, ) legTweaks.setConfig(server.configManager.vrConfig.legTweaks) localizer.setEnabled(humanPoseManager.getToggle(SkeletonConfigToggles.SELF_LOCALIZATION)) @@ -475,7 +472,7 @@ class HumanSkeleton( humanPoseManager.updateNodeOffsetsInSkeleton() // Update tap detection's trackers - tapDetectionManager.updateConfig(trackers) + tapDetectionManager?.refresh() // Rebuild Ik Solver ikSolver.buildChains(trackers) @@ -539,7 +536,7 @@ class HumanSkeleton( */ @VRServerThread fun updatePose() { - tapDetectionManager.update() + tapDetectionManager?.update() StayAligned.adjustNextTracker(trackerSkeleton, stayAlignedConfig) @@ -1556,7 +1553,7 @@ class HumanSkeleton( // Resets all axes of the trackers with the HMD as reference. for (tracker in trackersToReset) { // Only reset if tracker needsReset - if (tracker != null && (tracker.needsReset || tracker.isHmd) && (bodyParts.isEmpty() || bodyParts.contains(tracker.trackerPosition?.bodyPart))) { + if (tracker != null && (tracker.allowReset || tracker.isHmd) && (bodyParts.isEmpty() || bodyParts.contains(tracker.trackerPosition?.bodyPart))) { tracker.resetsHandler.resetFull(referenceRotation) } } @@ -1579,16 +1576,16 @@ class HumanSkeleton( var referenceRotation = IDENTITY headTracker?.let { if (bodyParts.isEmpty() || bodyParts.contains(BodyPart.HEAD)) { - // Only reset if head needsReset and isn't computed - if (it.needsReset && !it.isComputed) { + // Only reset if head allowReset and isn't computed + if (it.allowReset && !it.isComputed) { it.resetsHandler.resetYaw(referenceRotation) } } referenceRotation = it.getRotation() } for (tracker in trackersToReset) { - // Only reset if tracker needsReset - if (tracker != null && tracker.needsReset && (bodyParts.isEmpty() || bodyParts.contains(tracker.trackerPosition?.bodyPart))) { + // Only reset if tracker allowReset + if (tracker != null && tracker.allowReset && (bodyParts.isEmpty() || bodyParts.contains(tracker.trackerPosition?.bodyPart))) { tracker.resetsHandler.resetYaw(referenceRotation) } } @@ -1610,7 +1607,7 @@ class HumanSkeleton( // If there's a server present (required for status) and any tracker reports a // non-zero reset status (indicates reset required), then block mounting reset, // as it requires a full reset first - if (humanPoseManager.server != null && trackersToReset.any { it != null && it.lastResetStatus != 0u }) { + if (humanPoseManager.server != null && trackersToReset.any { it != null && it.needReset }) { LogManager.info("[HumanSkeleton] Reset: mounting ($resetSourceName) failed, reset required") return } @@ -1619,45 +1616,70 @@ class HumanSkeleton( var referenceRotation = IDENTITY headTracker?.let { if (bodyParts.isEmpty() || bodyParts.contains(BodyPart.HEAD)) { - // Only reset if head needsMounting or is computed but not HMD - if (it.needsMounting || (it.isComputed && !it.isHmd)) { + // Only reset if head allowMounting or is computed but not HMD + if (it.allowMounting || (it.isComputed && !it.isHmd)) { it.resetsHandler.resetMounting(referenceRotation) } } referenceRotation = it.getRotation() } - // If onlyFeet is true, feet will be forced to be mounting reset in their reset handlers. - val onlyFeet = bodyParts.isNotEmpty() && bodyParts.all { it == BodyPart.LEFT_FOOT || it == BodyPart.RIGHT_FOOT } for (tracker in trackersToReset) { // Only reset if tracker needsMounting - if (tracker != null && tracker.needsMounting && (bodyParts.isEmpty() || bodyParts.contains(tracker.trackerPosition?.bodyPart))) { - tracker.resetsHandler.resetMounting(referenceRotation, onlyFeet) + if (tracker != null && tracker.allowMounting && (bodyParts.isEmpty() || bodyParts.contains(tracker.trackerPosition?.bodyPart))) { + tracker.resetsHandler.resetMounting(referenceRotation) } } legTweaks.resetBuffer() localizer.reset() + + if (humanPoseManager.server != null) { + humanPoseManager.server.configManager.vrConfig.resetsConfig.lastMountingMethod = + MountingMethods.AUTOMATIC + if (!humanPoseManager.server.trackingChecklistManager.resetMountingCompleted) { + humanPoseManager.server.trackingChecklistManager.resetMountingCompleted = bodyParts.any { it -> + val defaultParts = if (humanPoseManager.server.configManager.vrConfig.resetsConfig.resetMountingFeet) { + TrackerUtils.allBodyPartsButFingers + } else { + TrackerUtils.allBodyPartsButFingersAndFeets + } + + return@any defaultParts.contains(it) + } + } + if (!humanPoseManager.server.trackingChecklistManager.feetResetMountingCompleted) { + humanPoseManager.server.trackingChecklistManager.feetResetMountingCompleted = bodyParts.any { TrackerUtils.feetsBodyParts.contains(it) } + } + humanPoseManager.server.configManager.saveConfig() + } + LogManager.info("[HumanSkeleton] Reset: mounting ($resetSourceName)") } @VRServerThread fun clearTrackersMounting(resetSourceName: String?) { headTracker?.let { - if (it.needsMounting) it.resetsHandler.clearMounting() + if (it.allowMounting) it.resetsHandler.clearMounting() } for (tracker in trackersToReset) { if (tracker != null && - tracker.needsMounting + tracker.allowMounting ) { tracker.resetsHandler.clearMounting() } } legTweaks.resetBuffer() LogManager.info("[HumanSkeleton] Clear: mounting ($resetSourceName)") + + if (humanPoseManager.server != null) { + humanPoseManager.server.trackingChecklistManager.resetMountingCompleted = false + humanPoseManager.server.trackingChecklistManager.feetResetMountingCompleted = false + humanPoseManager.server.configManager.saveConfig() + } } fun updateTapDetectionConfig() { - tapDetectionManager.updateConfig(null) + tapDetectionManager?.refresh() } fun updateLegTweaksConfig() { diff --git a/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/TapDetection.kt b/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/TapDetection.kt index 340888844..073709eab 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/TapDetection.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/TapDetection.kt @@ -4,73 +4,27 @@ import dev.slimevr.tracking.trackers.Tracker import java.util.* // class that monitors the acceleration of the waist, hip, chest or upper chest trackers to detect taps -// and use this to trigger a varaity of resets (if your wondering why no single tap class exists, it's because +// and use this to trigger a variety of resets (if your wondering why no single tap class exists, it's because // to many false positives) -class TapDetection { - // server and related classes - private val skeleton: HumanSkeleton? +class TapDetection(val skeleton: HumanSkeleton, val trackerToWatch: Tracker, val numberTrackersOverThreshold: Int, val tapToComplete: Int, val onTapCompleted: () -> Unit) { - // tap detection - @JvmField - var enabled: Boolean = false private val accelList = LinkedList() - private val tapTimes = LinkedList() - var tracker: Tracker? = null - private set - private var numberTrackersOverThreshold = 1 - - private var timeWindowNS = 0.6f * NS_CONVERTER - - // state - var detectionTime: Float = -1.0f - private set - var taps: Int = 0 - private set + private val tapTimestamps = LinkedList() + private var timeWindowNS = 0.3f * tapToComplete * NS_CONVERTER private var waitForLowAccel = false - constructor(skeleton: HumanSkeleton?) { - this.skeleton = skeleton - } - - constructor( - skeleton: HumanSkeleton?, - trackerToWatch: Tracker?, - ) { - this.skeleton = skeleton - this.tracker = trackerToWatch - } - - // set the tracker to watch and detect taps on - fun setTrackerToWatch(tracker: Tracker?) { - this.tracker = tracker - } - - fun setNumberTrackersOverThreshold(numberTrackersOverThreshold: Int) { - this.numberTrackersOverThreshold = numberTrackersOverThreshold - } - // reset the lists for detecting taps - fun resetDetector() { - tapTimes.clear() + fun reset() { + tapTimestamps.clear() accelList.clear() - taps = 0 - } - - // set the max taps this detector is configured to detect - fun setMaxTaps(maxTaps: Int) { - timeWindowNS = 0.3f * maxTaps * NS_CONVERTER + waitForLowAccel = false } // main function for tap detection fun update() { - if (skeleton == null || !enabled) return - - if (tracker == null) return - // get the acceleration of the tracker and add it to the list val time = System.nanoTime().toFloat() - val listval = floatArrayOf(tracker!!.getAcceleration().len(), time) - accelList.add(listval) + accelList.add(floatArrayOf(trackerToWatch.getAcceleration().len(), time)) // remove old values from the list (if they are too old) while (time - accelList.first()[1] > CLUMP_TIME_NS) { @@ -82,7 +36,7 @@ class TapDetection { if (accelDelta > NEEDED_ACCEL_DELTA && !waitForLowAccel) { // after a tap is added to the list, a lower acceleration // is needed before another tap can be added - tapTimes.add(time) + tapTimestamps.add(time) waitForLowAccel = true } @@ -92,25 +46,23 @@ class TapDetection { } // remove old taps from the list (if they are too old) - if (!tapTimes.isEmpty()) { - while (time - tapTimes.first() > timeWindowNS) { - tapTimes.removeFirst() - if (tapTimes.isEmpty()) return + if (!tapTimestamps.isEmpty()) { + while (time - tapTimestamps.first() > timeWindowNS) { + tapTimestamps.removeFirst() + if (tapTimestamps.isEmpty()) return } } - // if the user is moving their body too much, reset the tap list - if (!isUserStatic(tracker!!)) { - tapTimes.clear() - accelList.clear() + // if we have no taps within the timeframe or + // if the user is moving their body too much, reset the tap detector + if (!isUserStatic(trackerToWatch)) { + reset() + return } - // get the amount of taps in the list - // and set the detection time - val newTaps = tapTimes.size - if (newTaps > taps) { - taps = newTaps - detectionTime = time + if (tapTimestamps.size >= tapToComplete) { + onTapCompleted() + reset() } } @@ -141,7 +93,7 @@ class TapDetection { // you need two or more trackers for this feature to be reliable) private fun isUserStatic(trackerToExclude: Tracker): Boolean { var num = 0 - if (skeleton!!.upperChestTracker != null && + if (skeleton.upperChestTracker != null && skeleton.upperChestTracker != trackerToExclude ) { if (skeleton.upperChestTracker!!.getAcceleration().lenSq() diff --git a/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/TapDetectionManager.java b/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/TapDetectionManager.java deleted file mode 100644 index 404f38c6a..000000000 --- a/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/TapDetectionManager.java +++ /dev/null @@ -1,269 +0,0 @@ -package dev.slimevr.tracking.processor.skeleton; - - -import dev.slimevr.VRServer; -import dev.slimevr.config.TapDetectionConfig; -import dev.slimevr.reset.ResetHandler; -import dev.slimevr.setup.TapSetupHandler; -import dev.slimevr.tracking.processor.HumanPoseManager; -import dev.slimevr.tracking.trackers.Tracker; -import dev.slimevr.tracking.trackers.TrackerUtils; -import solarxr_protocol.rpc.ResetType; -import solarxr_protocol.rpc.StatusData; - -import java.util.ArrayList; -import java.util.List; - - -/** - * Handles tap detection for reset - */ -public class TapDetectionManager { - private static final String resetSourceName = "TapDetection"; - private static final int tapsForSetupMode = 2; - - // server and related classes - private final HumanSkeleton skeleton; - private HumanPoseManager humanPoseManager; - private TapDetectionConfig config; - - // tap detectors - private TapDetection yawResetDetector; - private TapDetection fullResetDetector; - private TapDetection mountingResetDetector; - - private ArrayList tapDetectors; - - // number of taps to detect - private int yawResetTaps = 2; - private int fullResetTaps = 3; - private int mountingResetTaps = 3; - - // delay - private static final float NS_CONVERTER = 1.0e9f; - private float fullResetDelayNs = 0.20f * NS_CONVERTER; - private float yawResetDelayNs = 1.00f * NS_CONVERTER; - private float mountingResetDelayNs = 1.00f * NS_CONVERTER; - - // feedback - private boolean yawResetAllowPlaySound = true; - private boolean fullResetAllowPlaySound = true; - private boolean mountingResetAllowPlaySound = true; - - private ResetHandler resetHandler; - private TapSetupHandler tapSetupHandler; - - public TapDetectionManager(HumanSkeleton skeleton) { - this.skeleton = skeleton; - } - - public TapDetectionManager( - HumanSkeleton skeleton, - HumanPoseManager humanPoseManager, - TapDetectionConfig config, - ResetHandler resetHandler, - TapSetupHandler tapSetupHandler, - List trackers - ) { - this.skeleton = skeleton; - this.humanPoseManager = humanPoseManager; - this.config = config; - this.resetHandler = resetHandler; - this.tapSetupHandler = tapSetupHandler; - - updateConfig(trackers); - } - - public void updateConfig(List trackers) { - // check the skeleton for new trackers - yawResetDetector = new TapDetection(skeleton, getTrackerToWatchYawReset()); - fullResetDetector = new TapDetection(skeleton, getTrackerToWatchFullReset()); - mountingResetDetector = new TapDetection(skeleton, getTrackerToWatchMountingReset()); - - if (trackers != null) { - tapDetectors = new ArrayList<>(); - for (Tracker tracker : trackers) { - TapDetection tapDetector = new TapDetection(skeleton, tracker); - tapDetector.enabled = true; - tapDetectors.add(tapDetector); - } - } - - if (this.config == null) { - return; - } - - this.yawResetDelayNs = config.getYawResetDelay() * NS_CONVERTER; - this.fullResetDelayNs = config.getFullResetDelay() * NS_CONVERTER; - this.mountingResetDelayNs = config.getMountingResetDelay() * NS_CONVERTER; - yawResetDetector.enabled = config.getYawResetEnabled(); - fullResetDetector.enabled = config.getFullResetEnabled(); - mountingResetDetector.enabled = config.getMountingResetEnabled(); - yawResetTaps = config.getYawResetTaps(); - fullResetTaps = config.getFullResetTaps(); - mountingResetTaps = config.getMountingResetTaps(); - yawResetDetector.setMaxTaps(yawResetTaps); - fullResetDetector.setMaxTaps(fullResetTaps); - mountingResetDetector.setMaxTaps(mountingResetTaps); - yawResetDetector - .setNumberTrackersOverThreshold( - config.getNumberTrackersOverThreshold() - ); - fullResetDetector - .setNumberTrackersOverThreshold( - config.getNumberTrackersOverThreshold() - ); - mountingResetDetector - .setNumberTrackersOverThreshold( - config.getNumberTrackersOverThreshold() - ); - } - - public void update() { - if ( - yawResetDetector == null - || fullResetDetector == null - || mountingResetDetector == null - || tapDetectors == null - || config == null - ) - return; - - // if setup mode is enabled, update the tap detectors for each tracker - if (config.getSetupMode()) { - for (TapDetection tapDetector : tapDetectors) { - tapDetector.update(); - - if (tapDetector.getTaps() >= tapsForSetupMode) { - tapSetupHandler.sendTap(tapDetector.getTracker()); - tapDetector.resetDetector(); - } - } - } else { - // update the tap detectors - yawResetDetector.update(); - fullResetDetector.update(); - mountingResetDetector.update(); - - // check if any tap detectors have detected taps - checkYawReset(); - checkFullReset(); - checkMountingReset(); - } - } - - private void checkYawReset() { - boolean tapped = (yawResetTaps <= yawResetDetector.getTaps()); - - if (tapped && yawResetAllowPlaySound) { - this.resetHandler.sendStarted(ResetType.Yaw); - yawResetAllowPlaySound = false; - } - - if ( - tapped && System.nanoTime() - yawResetDetector.getDetectionTime() > yawResetDelayNs - ) { - if (humanPoseManager != null) - humanPoseManager.resetTrackersYaw(resetSourceName); - else - skeleton.resetTrackersYaw(resetSourceName); - - yawResetDetector.resetDetector(); - yawResetAllowPlaySound = true; - this.resetHandler.sendFinished(ResetType.Yaw); - } - } - - private void checkFullReset() { - boolean tapped = (fullResetTaps <= fullResetDetector.getTaps()); - - if (tapped && fullResetAllowPlaySound) { - this.resetHandler.sendStarted(ResetType.Full); - fullResetAllowPlaySound = false; - } - - if ( - tapped && System.nanoTime() - fullResetDetector.getDetectionTime() > fullResetDelayNs - ) { - if (humanPoseManager != null) - humanPoseManager.resetTrackersFull(resetSourceName); - else - skeleton.resetTrackersFull(resetSourceName); - - fullResetDetector.resetDetector(); - fullResetAllowPlaySound = true; - this.resetHandler.sendFinished(ResetType.Full); - } - } - - private void checkMountingReset() { - // Don't allow mounting if tracker needs reset - VRServer server = humanPoseManager.getServer(); - if (server != null && server.statusSystem.hasStatusType(StatusData.StatusTrackerReset)) { - mountingResetDetector.resetDetector(); - return; - } - - boolean tapped = (mountingResetTaps <= mountingResetDetector.getTaps()); - - if (tapped && mountingResetAllowPlaySound) { - this.resetHandler.sendStarted(ResetType.Mounting); - mountingResetAllowPlaySound = false; - } - - if ( - tapped - && System.nanoTime() - mountingResetDetector.getDetectionTime() - > mountingResetDelayNs - ) { - // This will ask the skeleton for a mounting reset on every tracker - // except fingers. - // However, feet being reset or not will end up being decided on a - // per-tracker basis - // due to the setting being in ResetsConfig.kt - skeleton - .resetTrackersMounting( - resetSourceName, - TrackerUtils.INSTANCE.getAllBodyPartsButFingers() - ); - - mountingResetDetector.resetDetector(); - mountingResetAllowPlaySound = true; - this.resetHandler.sendFinished(ResetType.Mounting); - } - } - - - // returns either the chest tracker, hip tracker, or waist tracker depending - // on which one is available - // if none are available, returns null - private Tracker getTrackerToWatchYawReset() { - if (skeleton.getUpperChestTracker() != null) - return skeleton.getUpperChestTracker(); - else if (skeleton.getChestTracker() != null) - return skeleton.getChestTracker(); - else if (skeleton.getHipTracker() != null) - return skeleton.getHipTracker(); - else if (skeleton.getWaistTracker() != null) - return skeleton.getWaistTracker(); - else - return null; - } - - private Tracker getTrackerToWatchFullReset() { - if (skeleton.getLeftUpperLegTracker() != null) - return skeleton.getLeftUpperLegTracker(); - else if (skeleton.getLeftLowerLegTracker() != null) - return skeleton.getLeftLowerLegTracker(); - return null; - } - - private Tracker getTrackerToWatchMountingReset() { - if (skeleton.getRightUpperLegTracker() != null) - return skeleton.getRightUpperLegTracker(); - else if (skeleton.getRightLowerLegTracker() != null) - return skeleton.getRightLowerLegTracker(); - return null; - } - -} diff --git a/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/TapDetectionManager.kt b/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/TapDetectionManager.kt new file mode 100644 index 000000000..50f5b5e7c --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/tracking/processor/skeleton/TapDetectionManager.kt @@ -0,0 +1,116 @@ +package dev.slimevr.tracking.processor.skeleton + +import dev.slimevr.VRServer +import dev.slimevr.tracking.processor.HumanPoseManager +import dev.slimevr.tracking.trackers.Tracker + +class TapDetectionManager( + val server: VRServer, + val skeleton: HumanSkeleton, + val humanPoseManager: HumanPoseManager, +) { + private var tapDetectors: ArrayList = arrayListOf() + var yawResetDetector: TapDetection? = null + var fullResetDetector: TapDetection? = null + var mountingResetDetector: TapDetection? = null + + var config = server.configManager.vrConfig.tapDetection + + init { + refresh() + } + + fun registerSingleTapDetectors() { + for (tracker in server.allTrackers) { + tapDetectors.add( + TapDetection(skeleton, tracker, config.numberTrackersOverThreshold, 2) { + server.tapSetupHandler.sendTap(tracker) + }, + ) + } + } + + fun registerResetsDetectors() { + val yawTracker = yawResetTracker + yawResetDetector = if (yawTracker != null && config.yawResetEnabled) { + TapDetection(skeleton, yawTracker, config.numberTrackersOverThreshold, config.yawResetTaps) { + server.scheduleResetTrackersYaw(RESET_SOURCE_NAME, (config.yawResetDelay * 1000).toLong()) + } + } else { + null + } + + val fullTracker = fullResetTracker + fullResetDetector = if (fullTracker != null && config.fullResetEnabled) { + TapDetection(skeleton, fullTracker, config.numberTrackersOverThreshold, config.fullResetTaps) { + server.scheduleResetTrackersFull(RESET_SOURCE_NAME, (config.fullResetDelay * 1000).toLong()) + } + } else { + null + } + + val mountingTracker = mountingResetTracker + mountingResetDetector = if (mountingTracker != null && config.mountingResetEnabled) { + TapDetection(skeleton, mountingTracker, config.numberTrackersOverThreshold, config.mountingResetTaps) { + server.scheduleResetTrackersMounting(RESET_SOURCE_NAME, (config.mountingResetDelay * 1000).toLong()) + } + } else { + null + } + } + + /** + * Called when the list of available trackers gets updated + * or when the tap settings get changed + * it re-create the tap detectors according to the configs and available trackers + */ + fun refresh() { + tapDetectors.clear() + registerSingleTapDetectors() + registerResetsDetectors() + } + + fun update() { + // We disable the resets detectors during the assignment phase so you cant + // trigger a reset while assigning + if (config.setupMode) { + for (detector in tapDetectors) { + detector.update() + } + } else { + yawResetDetector?.update() + fullResetDetector?.update() + mountingResetDetector?.update() + } + } + + private val mountingResetTracker: Tracker? + get() { + return arrayOf( + skeleton.rightUpperLegTracker, + skeleton.rightLowerLegTracker, + ).firstNotNullOfOrNull { it } + } + + private val fullResetTracker: Tracker? + get() { + return arrayOf( + skeleton.leftUpperLegTracker, + skeleton.leftLowerLegTracker, + ).firstNotNullOfOrNull { it } + } + + private val yawResetTracker: Tracker? + get() { + return arrayOf( + skeleton.upperChestTracker, + skeleton.chestTracker, + skeleton.hipTracker, + skeleton.waistTracker, + ).firstNotNullOfOrNull { it } + } + + companion object { + const val RESET_SOURCE_NAME: String = "TapDetection" + } +} diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt index caa4bfadd..e88cfdffd 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/Tracker.kt @@ -11,12 +11,6 @@ import dev.slimevr.util.InterpolationHandler import io.eiren.util.BufferedTimer import io.github.axisangles.ktmath.Quaternion import io.github.axisangles.ktmath.Vector3 -import solarxr_protocol.datatypes.DeviceIdT -import solarxr_protocol.datatypes.TrackerIdT -import solarxr_protocol.rpc.StatusData -import solarxr_protocol.rpc.StatusDataUnion -import solarxr_protocol.rpc.StatusTrackerErrorT -import solarxr_protocol.rpc.StatusTrackerResetT import kotlin.properties.Delegates const val TIMEOUT_MS = 2_000L @@ -71,9 +65,23 @@ class Tracker @JvmOverloads constructor( * [trackRotDirection] is set to false. */ val allowFiltering: Boolean = false, - val needsReset: Boolean = false, - val needsMounting: Boolean = false, + + /** + * If true, the tracker can be reset + */ + val allowReset: Boolean = false, + /** + * If true, the tracker can do mounting calibration + */ + val allowMounting: Boolean = false, + val isHmd: Boolean = false, + + /** + * If true, the tracker need the user to perform a reset + */ + var needReset: Boolean = false, + /** * Whether to track the direction of the tracker's rotation * (positive vs negative rotation). This needs to be disabled for AutoBone and @@ -108,33 +116,27 @@ class Tracker @JvmOverloads constructor( var magStatus: MagnetometerStatus = magStatus private set + /** + * Watch the rest calibration status + */ + var hasCompletedRestCalibration: Boolean? = null + /** * If the tracker has gotten disconnected after it was initialized first time */ - var statusResetRecently = false - private var alreadyInitialized = false var status: TrackerStatus by Delegates.observable(TrackerStatus.DISCONNECTED) { _, old, new -> if (old == new) return@observable - if (!new.reset) { - if (alreadyInitialized) { - statusResetRecently = true - } - alreadyInitialized = true + if (allowReset && !old.reset && new.reset && !needReset) { + needReset = true } + if (!isInternal && VRServer.instanceInitialized) { // If the status of a non-internal tracker has changed, inform // the VRServer to recreate the skeleton, as it may need to // assign or un-assign the tracker to a body part VRServer.instance.updateSkeletonModel() VRServer.instance.refreshTrackersDriftCompensationEnabled() - - if (isHmd) { - VRServer.instance.humanPoseManager.checkReportMissingHmd() - } - checkReportErrorStatus() - checkReportRequireReset() - VRServer.instance.trackerStatusChanged(this, old, new) } } @@ -142,16 +144,18 @@ class Tracker @JvmOverloads constructor( var trackerPosition: TrackerPosition? by Delegates.observable(trackerPosition) { _, old, new -> if (old == new) return@observable + if (allowReset && !needReset) { + needReset = true + } + if (!isInternal) { // Set default mounting orientation for that body part new?.let { resetsHandler.mountingOrientation = it.defaultMounting() } - - checkReportRequireReset() } } // Computed value to simplify availability checks - val hasAdjustedRotation = hasRotation && (allowFiltering || needsReset) + val hasAdjustedRotation = hasRotation && (allowFiltering || allowReset) /** * It's like the ID, but it should be local to the device if it has one @@ -163,11 +167,11 @@ class Tracker @JvmOverloads constructor( init { // IMPORTANT: Look here for the required states of inputs - require(!needsReset || (hasRotation && needsReset)) { - "If ${::needsReset.name} is true, then ${::hasRotation.name} must also be true" + require(!allowReset || (hasRotation && allowReset)) { + "If ${::allowReset.name} is true, then ${::hasRotation.name} must also be true" } - require(!needsMounting || (needsReset && needsMounting)) { - "If ${::needsMounting.name} is true, then ${::needsReset.name} must also be true" + require(!allowMounting || (allowReset && allowMounting)) { + "If ${::allowMounting.name} is true, then ${::allowReset.name} must also be true" } require(!isHmd || (hasPosition && isHmd)) { "If ${::isHmd.name} is true, then ${::hasPosition.name} must also be true" @@ -177,76 +181,6 @@ class Tracker @JvmOverloads constructor( // } } - fun checkReportRequireReset() { - if (needsReset && - trackerPosition != null && - lastResetStatus == 0u && - !status.reset && - (isImu() || !statusResetRecently && trackerDataType != TrackerDataType.FLEX_ANGLE) - ) { - reportRequireReset() - } else if (lastResetStatus != 0u && (trackerPosition == null || status.reset)) { - VRServer.instance.statusSystem.removeStatus(lastResetStatus) - lastResetStatus = 0u - } - } - - /** - * If 0 then it's null - */ - var lastResetStatus = 0u - private fun reportRequireReset() { - require(lastResetStatus == 0u) { - "lastResetStatus must be 0u, but was $lastResetStatus" - } - - val tempTrackerNum = this.trackerNum - val statusMsg = StatusTrackerResetT().apply { - trackerId = TrackerIdT().apply { - if (device != null) { - deviceId = DeviceIdT().apply { id = device.id } - } - trackerNum = tempTrackerNum - } - } - val status = StatusDataUnion().apply { - type = StatusData.StatusTrackerReset - value = statusMsg - } - lastResetStatus = VRServer.instance.statusSystem.addStatus(status, true) - } - - private fun checkReportErrorStatus() { - if (status == TrackerStatus.ERROR && lastErrorStatus == 0u) { - reportErrorStatus() - } else if (lastErrorStatus != 0u && status != TrackerStatus.ERROR) { - VRServer.instance.statusSystem.removeStatus(lastErrorStatus) - lastErrorStatus = 0u - } - } - - var lastErrorStatus = 0u - private fun reportErrorStatus() { - require(lastErrorStatus == 0u) { - "lastResetStatus must be 0u, but was $lastErrorStatus" - } - - val tempTrackerNum = this.trackerNum - val statusMsg = StatusTrackerErrorT().apply { - trackerId = TrackerIdT().apply { - if (device != null) { - deviceId = DeviceIdT().apply { id = device.id } - } - trackerNum = tempTrackerNum - } - } - val status = StatusDataUnion().apply { - type = StatusData.StatusTrackerError - value = statusMsg - } - lastErrorStatus = VRServer.instance.statusSystem.addStatus(status, true) - } - /** * Reads/loads from the given config */ @@ -257,7 +191,7 @@ class Tracker @JvmOverloads constructor( config.designation?.let { designation -> getByDesignation(designation)?.let { trackerPosition = it } } ?: run { trackerPosition = null } - if (needsMounting) { + if (allowMounting) { // Load manual mounting config.mountingOrientation?.let { resetsHandler.mountingOrientation = it.toValue() } } @@ -271,11 +205,6 @@ class Tracker @JvmOverloads constructor( resetsHandler.allowDriftCompensation = it } } - if (!isInternal && - !(!isImu() && (trackerPosition == TrackerPosition.LEFT_HAND || trackerPosition == TrackerPosition.RIGHT_HAND)) - ) { - checkReportRequireReset() - } } /** @@ -284,7 +213,7 @@ class Tracker @JvmOverloads constructor( fun writeConfig(config: TrackerConfig) { trackerPosition?.let { config.designation = it.designation } ?: run { config.designation = null } customName?.let { config.customName = it } - if (needsMounting) { + if (allowMounting) { // Save manual mounting config.mountingOrientation = resetsHandler.mountingOrientation.toObject() } @@ -364,7 +293,7 @@ class Tracker @JvmOverloads constructor( } // Reset if needed and is not computed and internal - return if (needsReset && !(isComputed && isInternal) && trackerDataType == TrackerDataType.ROTATION) { + return if (allowReset && !(isComputed && isInternal) && trackerDataType == TrackerDataType.ROTATION) { // Adjust to reset, mounting and drift compensation resetsHandler.getReferenceAdjustedDriftRotationFrom(rot) } else { @@ -384,7 +313,7 @@ class Tracker @JvmOverloads constructor( rot = Quaternion.rotationAroundYAxis(stayAligned.yawCorrection.toRad()) * rot // Reset if needed and is not computed and internal - return if (needsReset && !(isComputed && isInternal) && trackerDataType == TrackerDataType.ROTATION) { + return if (needReset && !(isComputed && isInternal) && trackerDataType == TrackerDataType.ROTATION) { // Adjust to reset, mounting and drift compensation resetsHandler.getReferenceAdjustedDriftRotationFrom(rot) } else { @@ -406,7 +335,7 @@ class Tracker @JvmOverloads constructor( } // Reset if needed or is a computed tracker besides head - return if (needsReset && !(isComputed && trackerPosition != TrackerPosition.HEAD) && trackerDataType == TrackerDataType.ROTATION) { + return if (allowReset && !(isComputed && trackerPosition != TrackerPosition.HEAD) && trackerDataType == TrackerDataType.ROTATION) { // Adjust to reset and mounting resetsHandler.getIdentityAdjustedDriftRotationFrom(rot) } else { @@ -442,7 +371,7 @@ class Tracker @JvmOverloads constructor( /** * Gets the world-adjusted acceleration */ - fun getAcceleration(): Vector3 = if (needsReset) { + fun getAcceleration(): Vector3 = if (allowReset) { resetsHandler.getReferenceAdjustedAccel(_rotation, _acceleration) } else { _acceleration @@ -488,7 +417,7 @@ class Tracker @JvmOverloads constructor( /** * Gets the magnetic field vector, in mGauss. */ - fun getMagVector() = if (needsReset) { + fun getMagVector() = if (allowReset) { resetsHandler.getReferenceAdjustedAccel(_rotation, _magVector) } else { _magVector diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/TrackerResetsHandler.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/TrackerResetsHandler.kt index 6b6c962cc..ddc5e1b2e 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/TrackerResetsHandler.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/TrackerResetsHandler.kt @@ -15,10 +15,7 @@ import kotlin.math.* private const val DRIFT_COOLDOWN_MS = 50000L -/** - * Class taking care of full reset, yaw reset, mounting reset, - * and drift compensation logic. - */ +/** Class taking care of full reset, yaw reset, mounting reset, and drift compensation logic. */ class TrackerResetsHandler(val tracker: Tracker) { private val HalfHorizontal = EulerAngles( @@ -38,7 +35,6 @@ class TrackerResetsHandler(val tracker: Tracker) { private var compensateDrift = false private var driftPrediction = false private var driftCompensationEnabled = false - private var resetMountingFeet = false private var armsResetMode = ArmsResetModes.BACK private var yawResetSmoothTime = 0.0f var saveMountingReset = false @@ -165,7 +161,6 @@ class TrackerResetsHandler(val tracker: Tracker) { * Reads/loads reset settings from the given config */ fun readResetConfig(config: ResetsConfig) { - resetMountingFeet = config.resetMountingFeet armsResetMode = config.mode yawResetSmoothTime = config.yawResetSmoothTime saveMountingReset = config.saveMountingReset @@ -275,7 +270,7 @@ class TrackerResetsHandler(val tracker: Tracker) { val mountingAdjustedRotation = tracker.getRawRotation() * mountingOrientation // Gyrofix - if (tracker.needsMounting || (tracker.trackerPosition == TrackerPosition.HEAD && !tracker.isHmd)) { + if (tracker.allowMounting || (tracker.trackerPosition == TrackerPosition.HEAD && !tracker.isHmd)) { gyroFix = if (tracker.isComputed) { fixGyroscope(tracker.getRawRotation()) } else { @@ -307,7 +302,7 @@ class TrackerResetsHandler(val tracker: Tracker) { } // Rotate attachmentFix by 180 degrees as a workaround for t-pose (down) - if (tposeDownFix != Quaternion.IDENTITY && tracker.needsMounting) { + if (tposeDownFix != Quaternion.IDENTITY && tracker.allowMounting) { attachmentFix *= HalfHorizontal } @@ -329,9 +324,8 @@ class TrackerResetsHandler(val tracker: Tracker) { } private fun postProcessResetFull(reference: Quaternion) { - if (this.tracker.lastResetStatus != 0u) { - VRServer.instance.statusSystem.removeStatus(this.tracker.lastResetStatus) - this.tracker.lastResetStatus = 0u + if (this.tracker.needReset) { + this.tracker.needReset = false } tracker.resetFilteringQuats(reference) @@ -376,13 +370,7 @@ class TrackerResetsHandler(val tracker: Tracker) { ) } - // Remove the status if yaw reset was performed after the tracker - // was disconnected and connected. - if (this.tracker.lastResetStatus != 0u && this.tracker.statusResetRecently) { - VRServer.instance.statusSystem.removeStatus(this.tracker.lastResetStatus) - this.tracker.statusResetRecently = false - this.tracker.lastResetStatus = 0u - } + this.tracker.needReset = false // Reset Stay Aligned (before resetting filtering, which depends on the // tracker's rotation) @@ -394,17 +382,14 @@ class TrackerResetsHandler(val tracker: Tracker) { /** * Perform the math to align the tracker to go forward * and stores it in mountRotFix, and adjusts yawFix - * If forceFeet is true, always reset feet regardless of resetMountingFeet's value. */ - fun resetMounting(reference: Quaternion, forceFeet: Boolean = false) { + fun resetMounting(reference: Quaternion) { if (tracker.trackerDataType == TrackerDataType.FLEX_RESISTANCE) { tracker.trackerFlexHandler.resetMax() tracker.resetFilteringQuats(reference) return } else if (tracker.trackerDataType == TrackerDataType.FLEX_ANGLE) { return - } else if (!resetMountingFeet && tracker.trackerPosition.isFoot() && !forceFeet) { - return } constraintFix = Quaternion.IDENTITY diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/TrackerUtils.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/TrackerUtils.kt index 8aa27052d..f3e73b3ee 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/TrackerUtils.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/TrackerUtils.kt @@ -102,4 +102,18 @@ object TrackerUtils { BodyPart.RIGHT_HAND, BodyPart.LEFT_SHOULDER, BodyPart.RIGHT_SHOULDER, BodyPart.LEFT_FOOT, BodyPart.RIGHT_FOOT, ) + + val allBodyPartsButFingersAndFeets = listOf( + BodyPart.HEAD, BodyPart.NECK, BodyPart.UPPER_CHEST, + BodyPart.CHEST, BodyPart.WAIST, BodyPart.HIP, + BodyPart.LEFT_UPPER_LEG, BodyPart.RIGHT_UPPER_LEG, BodyPart.LEFT_LOWER_LEG, + BodyPart.RIGHT_LOWER_LEG, BodyPart.LEFT_LOWER_ARM, BodyPart.RIGHT_LOWER_ARM, + BodyPart.LEFT_UPPER_ARM, BodyPart.RIGHT_UPPER_ARM, BodyPart.LEFT_HAND, + BodyPart.RIGHT_HAND, BodyPart.LEFT_SHOULDER, BodyPart.RIGHT_SHOULDER, + ) + + val feetsBodyParts = listOf( + BodyPart.LEFT_FOOT, + BodyPart.RIGHT_FOOT, + ) } diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt index 6e18e05ec..79879fa41 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/hid/HIDCommon.kt @@ -90,8 +90,8 @@ class HIDCommon { userEditable = true, imuType = sensorType, allowFiltering = true, - needsReset = true, - needsMounting = true, + allowReset = true, + allowMounting = true, usesTimeout = false, magStatus = magStatus, ) diff --git a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/TrackersUDPServer.kt b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/TrackersUDPServer.kt index 7f25b97b7..343b2ae24 100644 --- a/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/TrackersUDPServer.kt +++ b/server/core/src/main/java/dev/slimevr/tracking/trackers/udp/TrackersUDPServer.kt @@ -174,7 +174,16 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker // Set up new sensor for older firmware. // Firmware after 7 should send sensor status packet and sensor // will be created when it's received - setUpSensor(connection, 0, handshake.imuType, 1, MagnetometerStatus.NOT_SUPPORTED, null, TrackerDataType.ROTATION) + setUpSensor( + connection, + 0, + handshake.imuType, + 1, + MagnetometerStatus.NOT_SUPPORTED, + null, + TrackerDataType.ROTATION, + null, + ) } connection } @@ -186,7 +195,16 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker } private val mainScope = CoroutineScope(SupervisorJob()) - private fun setUpSensor(connection: UDPDevice, trackerId: Int, sensorType: IMUType, sensorStatus: Int, magStatus: MagnetometerStatus, trackerPosition: TrackerPosition?, trackerDataType: TrackerDataType) { + private fun setUpSensor( + connection: UDPDevice, + trackerId: Int, + sensorType: IMUType, + sensorStatus: Int, + magStatus: MagnetometerStatus, + trackerPosition: TrackerPosition?, + trackerDataType: TrackerDataType, + hasCompletedRestCalibration: Boolean?, + ) { LogManager.info("[TrackerServer] Sensor $trackerId for ${connection.name} status: $sensorStatus") var imuTracker = connection.getTracker(trackerId) if (imuTracker == null) { @@ -210,8 +228,8 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker userEditable = true, imuType = if (trackerDataType == TrackerDataType.ROTATION) sensorType else null, allowFiltering = true, - needsReset = true, - needsMounting = true, + allowReset = true, + allowMounting = true, usesTimeout = true, magStatus = magStatus, trackerDataType = trackerDataType, @@ -223,6 +241,8 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker val status = UDPPacket15SensorInfo.getStatus(sensorStatus) if (status != null) imuTracker.status = status + imuTracker.hasCompletedRestCalibration = hasCompletedRestCalibration + if (magStatus == MagnetometerStatus.NOT_SUPPORTED) return if (magStatus == MagnetometerStatus.ENABLED && (!VRServer.instance.configManager.vrConfig.server.useMagnetometerOnAllTrackers || imuTracker.config.shouldHaveMagEnabled == false) @@ -484,6 +504,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker magStatus, packet.trackerPosition, packet.trackerDataType, + packet.hasCompletedRestCalibration, ) // Send ack bb.limit(bb.capacity()) @@ -510,14 +531,15 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker when (packet.type) { UDPPacket21UserAction.RESET_FULL -> { name = "Full reset" - VRServer.instance.resetHandler.sendStarted(ResetType.Full) - VRServer.instance.resetTrackersFull(RESET_SOURCE_NAME) + VRServer.instance.scheduleResetTrackersFull( + RESET_SOURCE_NAME, + (VRServer.instance.configManager.vrConfig.resetsConfig.fullResetDelay * 1000).toLong(), + ) } UDPPacket21UserAction.RESET_YAW -> { name = "Yaw reset" - VRServer.instance.resetHandler.sendStarted(ResetType.Yaw) - VRServer.instance.resetTrackersYaw(RESET_SOURCE_NAME) + VRServer.instance.scheduleResetTrackersYaw(RESET_SOURCE_NAME, (VRServer.instance.configManager.vrConfig.resetsConfig.yawResetDelay * 1000).toLong()) } UDPPacket21UserAction.RESET_MOUNTING -> { @@ -526,7 +548,7 @@ class TrackersUDPServer(private val port: Int, name: String, private val tracker .instance .resetHandler .sendStarted(ResetType.Mounting) - VRServer.instance.resetTrackersMounting(RESET_SOURCE_NAME) + VRServer.instance.scheduleResetTrackersMounting(RESET_SOURCE_NAME, (VRServer.instance.configManager.vrConfig.resetsConfig.mountingResetDelay * 1000).toLong()) } UDPPacket21UserAction.PAUSE_TRACKING -> { diff --git a/server/core/src/main/java/dev/slimevr/trackingchecklist/TrackingChecklistManager.kt b/server/core/src/main/java/dev/slimevr/trackingchecklist/TrackingChecklistManager.kt new file mode 100644 index 000000000..4ac2ba608 --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/trackingchecklist/TrackingChecklistManager.kt @@ -0,0 +1,346 @@ +package dev.slimevr.trackingchecklist + +import dev.slimevr.VRServer +import dev.slimevr.bridge.ISteamVRBridge +import dev.slimevr.config.MountingMethods +import dev.slimevr.games.vrchat.VRCConfigListener +import dev.slimevr.games.vrchat.VRCConfigRecommendedValues +import dev.slimevr.games.vrchat.VRCConfigValidity +import dev.slimevr.games.vrchat.VRCConfigValues +import dev.slimevr.tracking.trackers.Tracker +import dev.slimevr.tracking.trackers.TrackerStatus +import dev.slimevr.tracking.trackers.TrackerUtils +import dev.slimevr.tracking.trackers.udp.TrackerDataType +import solarxr_protocol.datatypes.DeviceIdT +import solarxr_protocol.datatypes.TrackerIdT +import solarxr_protocol.rpc.* +import java.util.* +import java.util.concurrent.CopyOnWriteArrayList +import kotlin.concurrent.timerTask + +interface TrackingChecklistListener { + fun onStepsUpdate() +} + +class TrackingChecklistManager(private val vrServer: VRServer) : VRCConfigListener { + + private val listeners: MutableList = CopyOnWriteArrayList() + val steps: MutableList = mutableListOf() + + private val updateTrackingChecklistTimer = Timer("TrackingChecklistTimer") + + // Simple flag set to true if reset mounting was performed at least once. + // This value is only runtime and never saved + var resetMountingCompleted = false + var feetResetMountingCompleted = false + + init { + vrServer.vrcConfigManager.addListener(this) + + createSteps() + updateTrackingChecklistTimer.scheduleAtFixedRate( + timerTask { + updateChecklist() + }, + 0, + 1000, + ) + } + + fun addListener(channel: TrackingChecklistListener) { + listeners.add(channel) + } + + fun removeListener(channel: TrackingChecklistListener) { + listeners.removeIf { channel == it } + } + + fun buildTrackersIds(trackers: List): Array = trackers.map { tracker -> + TrackerIdT().apply { + if (tracker.device != null) { + deviceId = DeviceIdT().apply { id = tracker.device.id } + } + trackerNum = tracker.trackerNum + } + }.toTypedArray() + + private fun createSteps() { + steps.add( + TrackingChecklistStepT().apply { + id = TrackingChecklistStepId.NETWORK_PROFILE_PUBLIC + enabled = vrServer.networkProfileChecker.isSupported + optional = false + ignorable = true + visibility = TrackingChecklistStepVisibility.WHEN_INVALID + }, + ) + + steps.add( + TrackingChecklistStepT().apply { + id = TrackingChecklistStepId.STEAMVR_DISCONNECTED + enabled = true + optional = false + ignorable = true + visibility = TrackingChecklistStepVisibility.WHEN_INVALID + }, + ) + + steps.add( + TrackingChecklistStepT().apply { + id = TrackingChecklistStepId.TRACKER_ERROR + valid = true // Default to valid + enabled = true + optional = false + ignorable = false + visibility = TrackingChecklistStepVisibility.WHEN_INVALID + }, + ) + + steps.add( + TrackingChecklistStepT().apply { + id = TrackingChecklistStepId.TRACKERS_REST_CALIBRATION + enabled = true + optional = false + ignorable = true + visibility = TrackingChecklistStepVisibility.ALWAYS + }, + ) + + steps.add( + TrackingChecklistStepT().apply { + id = TrackingChecklistStepId.FULL_RESET + enabled = true + optional = false + ignorable = false + visibility = TrackingChecklistStepVisibility.ALWAYS + }, + ) + + steps.add( + TrackingChecklistStepT().apply { + id = TrackingChecklistStepId.MOUNTING_CALIBRATION + valid = false + enabled = vrServer.configManager.vrConfig.resetsConfig.lastMountingMethod == MountingMethods.AUTOMATIC + optional = false + ignorable = true + visibility = TrackingChecklistStepVisibility.ALWAYS + }, + ) + + steps.add( + TrackingChecklistStepT().apply { + id = TrackingChecklistStepId.FEET_MOUNTING_CALIBRATION + valid = false + enabled = false + optional = false + ignorable = true + visibility = TrackingChecklistStepVisibility.ALWAYS + }, + ) + + steps.add( + TrackingChecklistStepT().apply { + id = TrackingChecklistStepId.UNASSIGNED_HMD + enabled = true + optional = false + ignorable = false + visibility = TrackingChecklistStepVisibility.WHEN_INVALID + }, + ) + + steps.add( + TrackingChecklistStepT().apply { + id = TrackingChecklistStepId.STAY_ALIGNED_CONFIGURED + enabled = true + optional = true + ignorable = true + visibility = TrackingChecklistStepVisibility.WHEN_INVALID + }, + ) + + steps.add( + TrackingChecklistStepT().apply { + id = TrackingChecklistStepId.VRCHAT_SETTINGS + enabled = vrServer.vrcConfigManager.isSupported + optional = true + ignorable = true + visibility = TrackingChecklistStepVisibility.WHEN_INVALID + }, + ) + } + + fun updateChecklist() { + val assignedTrackers = + vrServer.allTrackers.filter { it.trackerPosition != null && it.status != TrackerStatus.DISCONNECTED } + val imuTrackers = + assignedTrackers.filter { it.isImu() && it.trackerDataType != TrackerDataType.FLEX_ANGLE } + + val trackersWithError = + imuTrackers.filter { it.status == TrackerStatus.ERROR } + updateValidity( + TrackingChecklistStepId.TRACKER_ERROR, + trackersWithError.isEmpty(), + ) { + if (trackersWithError.isNotEmpty()) { + it.extraData = TrackingChecklistExtraDataUnion().apply { + type = TrackingChecklistExtraData.TrackingChecklistTrackerError + value = TrackingChecklistTrackerErrorT().apply { + trackersId = buildTrackersIds(trackersWithError) + } + } + } else { + it.extraData = null + } + } + + val trackerRequireReset = imuTrackers.filter { + it.status !== TrackerStatus.ERROR && !it.isInternal && it.allowReset && it.needReset + } + updateValidity(TrackingChecklistStepId.FULL_RESET, trackerRequireReset.isEmpty()) { + if (trackerRequireReset.isNotEmpty()) { + it.extraData = TrackingChecklistExtraDataUnion().apply { + type = TrackingChecklistExtraData.TrackingChecklistTrackerReset + value = TrackingChecklistTrackerResetT().apply { + trackersId = buildTrackersIds(trackerRequireReset) + } + } + resetMountingCompleted = false + feetResetMountingCompleted = false + } else { + it.extraData = null + } + } + + val hmd = + assignedTrackers.firstOrNull { it.isHmd && !it.isInternal && it.status.sendData } + val assignedHmd = hmd == null || vrServer.humanPoseManager.skeleton.headTracker != null + updateValidity(TrackingChecklistStepId.UNASSIGNED_HMD, assignedHmd) { + if (!assignedHmd) { + it.extraData = TrackingChecklistExtraDataUnion().apply { + type = TrackingChecklistExtraData.TrackingChecklistUnassignedHMD + value = TrackingChecklistUnassignedHMDT().apply { + trackerId = TrackerIdT().apply { + if (hmd.device != null) { + deviceId = DeviceIdT().apply { id = hmd.device.id } + } + trackerNum = hmd.trackerNum + } + } + } + } else { + it.extraData = null + } + } + + val trackersNeedCalibration = imuTrackers.filter { + it.hasCompletedRestCalibration == false + } + updateValidity( + TrackingChecklistStepId.TRACKERS_REST_CALIBRATION, + trackersNeedCalibration.isEmpty(), + ) { + // Don't show the step if none of the trackers connected support IMU calibration + it.enabled = imuTrackers.any { t -> + t.hasCompletedRestCalibration != null + } + if (trackersNeedCalibration.isNotEmpty()) { + it.extraData = TrackingChecklistExtraDataUnion().apply { + type = TrackingChecklistExtraData.TrackingChecklistNeedCalibration + value = TrackingChecklistNeedCalibrationT().apply { + trackersId = buildTrackersIds(trackersNeedCalibration) + } + } + } else { + it.extraData = null + } + } + + val steamVRBridge = vrServer.getVRBridge(ISteamVRBridge::class.java) + if (steamVRBridge != null) { + val steamvrConnected = steamVRBridge.isConnected() + updateValidity( + TrackingChecklistStepId.STEAMVR_DISCONNECTED, + steamvrConnected, + ) { + if (!steamvrConnected) { + it.extraData = TrackingChecklistExtraDataUnion().apply { + type = TrackingChecklistExtraData.TrackingChecklistSteamVRDisconnected + value = TrackingChecklistSteamVRDisconnectedT().apply { + bridgeSettingsName = steamVRBridge.getBridgeConfigKey() + } + } + } else { + it.extraData = null + } + } + } + + if (vrServer.networkProfileChecker.isSupported) { + updateValidity(TrackingChecklistStepId.NETWORK_PROFILE_PUBLIC, vrServer.networkProfileChecker.publicNetworks.isEmpty()) { + if (vrServer.networkProfileChecker.publicNetworks.isNotEmpty()) { + it.extraData = TrackingChecklistExtraDataUnion().apply { + type = TrackingChecklistExtraData.TrackingChecklistPublicNetworks + value = TrackingChecklistPublicNetworksT().apply { + adapters = vrServer.networkProfileChecker.publicNetworks.map { it.name }.toTypedArray() + } + } + } else { + it.extraData = null + } + } + } + + updateValidity(TrackingChecklistStepId.MOUNTING_CALIBRATION, resetMountingCompleted) { + it.enabled = vrServer.configManager.vrConfig.resetsConfig.lastMountingMethod == MountingMethods.AUTOMATIC + } + + updateValidity(TrackingChecklistStepId.FEET_MOUNTING_CALIBRATION, feetResetMountingCompleted) { + it.enabled = + vrServer.configManager.vrConfig.resetsConfig.lastMountingMethod == MountingMethods.AUTOMATIC && + !vrServer.configManager.vrConfig.resetsConfig.resetMountingFeet && + imuTrackers.any { t -> TrackerUtils.feetsBodyParts.contains(t.trackerPosition?.bodyPart) } + } + + updateValidity(TrackingChecklistStepId.STAY_ALIGNED_CONFIGURED, vrServer.configManager.vrConfig.stayAlignedConfig.enabled) + + listeners.forEach { it.onStepsUpdate() } + } + + private fun updateValidity(id: Int, valid: Boolean, beforeUpdate: ((step: TrackingChecklistStepT) -> Unit)? = null) { + require(id != TrackingChecklistStepId.UNKNOWN) { + "id is unknown" + } + val step = steps.find { it.id == id } ?: return + step.valid = valid + if (beforeUpdate != null) { + beforeUpdate(step) + } + } + + override fun onChange( + validity: VRCConfigValidity, + values: VRCConfigValues, + recommended: VRCConfigRecommendedValues, + muted: List, + ) { + updateValidity( + TrackingChecklistStepId.VRCHAT_SETTINGS, + VRCConfigValidity::class.java.declaredFields.asSequence().all { p -> + p.isAccessible = true + return@all p.get(validity) == true || muted.contains(p.name) + }, + ) + listeners.forEach { it.onStepsUpdate() } + } + + fun ignoreStep(step: TrackingChecklistStepT, ignore: Boolean) { + if (!step.ignorable) return + val ignoredSteps = vrServer.configManager.vrConfig.trackingChecklist.ignoredStepsIds + if (ignore && !ignoredSteps.contains(step.id)) { + ignoredSteps.add(step.id) + } else if (!ignore) { + ignoredSteps.remove(step.id) + } + vrServer.configManager.saveConfig() + } +} diff --git a/server/core/src/test/java/dev/slimevr/unit/LegTweaksTests.kt b/server/core/src/test/java/dev/slimevr/unit/LegTweaksTests.kt index 4676adc5f..830a60f9d 100644 --- a/server/core/src/test/java/dev/slimevr/unit/LegTweaksTests.kt +++ b/server/core/src/test/java/dev/slimevr/unit/LegTweaksTests.kt @@ -26,8 +26,8 @@ class LegTweaksTests { hasRotation = true, isComputed = true, imuType = null, - needsReset = false, - needsMounting = false, + allowReset = false, + allowMounting = false, isHmd = true, trackRotDirection = false, ) diff --git a/server/core/src/test/java/dev/slimevr/unit/MountingResetTests.kt b/server/core/src/test/java/dev/slimevr/unit/MountingResetTests.kt index c046ab914..c7de28c0d 100644 --- a/server/core/src/test/java/dev/slimevr/unit/MountingResetTests.kt +++ b/server/core/src/test/java/dev/slimevr/unit/MountingResetTests.kt @@ -45,8 +45,8 @@ class MountingResetTests { null, hasRotation = true, imuType = IMUType.UNKNOWN, - needsReset = true, - needsMounting = true, + allowReset = true, + allowMounting = true, trackRotDirection = false, ) @@ -130,8 +130,8 @@ class MountingResetTests { null, hasRotation = true, imuType = IMUType.UNKNOWN, - needsReset = true, - needsMounting = true, + allowReset = true, + allowMounting = true, trackRotDirection = false, ) diff --git a/server/core/src/test/java/dev/slimevr/unit/ReferenceAdjustmentsTests.kt b/server/core/src/test/java/dev/slimevr/unit/ReferenceAdjustmentsTests.kt index a28d8c85e..5a90b1ddb 100644 --- a/server/core/src/test/java/dev/slimevr/unit/ReferenceAdjustmentsTests.kt +++ b/server/core/src/test/java/dev/slimevr/unit/ReferenceAdjustmentsTests.kt @@ -93,7 +93,7 @@ class ReferenceAdjustmentsTests { null, hasRotation = true, imuType = IMUType.UNKNOWN, - needsReset = true, + allowReset = true, ) tracker.setRotation(trackerQuat) tracker.resetsHandler.resetFull(referenceQuat) @@ -125,7 +125,7 @@ class ReferenceAdjustmentsTests { null, hasRotation = true, imuType = IMUType.UNKNOWN, - needsReset = true, + allowReset = true, ) tracker.setRotation(trackerQuat) tracker.resetsHandler.resetYaw(referenceQuat) @@ -153,7 +153,7 @@ class ReferenceAdjustmentsTests { null, hasRotation = true, imuType = IMUType.UNKNOWN, - needsReset = true, + allowReset = true, ) tracker.setRotation(trackerQuat) tracker.resetsHandler.resetFull(referenceQuat) diff --git a/server/core/src/test/java/dev/slimevr/unit/TestTrackerSet.kt b/server/core/src/test/java/dev/slimevr/unit/TestTrackerSet.kt index 92ec22b60..cac3f3851 100644 --- a/server/core/src/test/java/dev/slimevr/unit/TestTrackerSet.kt +++ b/server/core/src/test/java/dev/slimevr/unit/TestTrackerSet.kt @@ -58,8 +58,8 @@ class TestTrackerSet( hasPosition = positional || isHmd, hasRotation = true, isComputed = computed || isHmd, - needsReset = resetHead || !isHmd, - needsMounting = resetHead || !isHmd, + allowReset = resetHead || !isHmd, + allowMounting = resetHead || !isHmd, isHmd = isHmd, trackRotDirection = false, ) diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/NetworkProfileChecker.kt b/server/desktop/src/main/java/dev/slimevr/desktop/DesktopNetworkProfileChecker.kt similarity index 78% rename from server/desktop/src/main/java/dev/slimevr/desktop/NetworkProfileChecker.kt rename to server/desktop/src/main/java/dev/slimevr/desktop/DesktopNetworkProfileChecker.kt index 0655c4023..3fbf79667 100644 --- a/server/desktop/src/main/java/dev/slimevr/desktop/NetworkProfileChecker.kt +++ b/server/desktop/src/main/java/dev/slimevr/desktop/DesktopNetworkProfileChecker.kt @@ -13,60 +13,15 @@ import com.sun.jna.platform.win32.WTypes import com.sun.jna.platform.win32.WinNT.HRESULT import com.sun.jna.ptr.IntByReference import com.sun.jna.ptr.PointerByReference +import dev.slimevr.ConnectivityFlags +import dev.slimevr.NetworkCategory +import dev.slimevr.NetworkInfo +import dev.slimevr.NetworkProfileChecker import dev.slimevr.VRServer import io.eiren.util.OperatingSystem -import solarxr_protocol.rpc.StatusData -import solarxr_protocol.rpc.StatusDataUnion -import solarxr_protocol.rpc.StatusPublicNetworkT import java.util.* import kotlin.concurrent.scheduleAtFixedRate -data class NetworkInfo( - val name: String?, - val description: String?, - val category: NetworkCategory?, - val connectivity: Set?, - val connected: Boolean?, -) - -/** - * @see NLM_NETWORK_CATEGORY enumeration (netlistmgr.h) - */ -enum class NetworkCategory(val value: Int) { - PUBLIC(0), - PRIVATE(1), - DOMAIN_AUTHENTICATED(2), - ; - - companion object { - fun fromInt(value: Int) = values().find { it.value == value } - } -} - -/** - * @see NLM_CONNECTIVITY enumeration (netlistmgr.h) - */ -enum class ConnectivityFlags(val value: Int) { - DISCONNECTED(0), - IPV4_NOTRAFFIC(0x1), - IPV6_NOTRAFFIC(0x2), - IPV4_SUBNET(0x10), - IPV4_LOCALNETWORK(0x20), - IPV4_INTERNET(0x40), - IPV6_SUBNET(0x100), - IPV6_LOCALNETWORK(0x200), - IPV6_INTERNET(0x400), - ; - - companion object { - fun fromInt(value: Int): Set = if (value == 0) { - setOf(DISCONNECTED) - } else { - values().filter { it != DISCONNECTED && (value and it.value) != 0 }.toSet() - } - } -} - /** * @see INetworkConnection interface (netlistmgr.h) */ @@ -308,38 +263,22 @@ fun enumerateNetworks(): List? { return null } -class NetworkProfileChecker(private val server: VRServer) { +class DesktopNetworkProfileChecker(private val server: VRServer) : NetworkProfileChecker() { private val updateTickTimer = Timer("NetworkProfileCheck") - private var lastPublicNetworkStatus: UInt = 0u - private var numPublicNetworks = 0 + private var publicNetworksLocal: List = listOf() + + override val isSupported: Boolean + get() = OperatingSystem.currentPlatform == OperatingSystem.WINDOWS + + override val publicNetworks: List + get() = publicNetworksLocal init { if (OperatingSystem.currentPlatform == OperatingSystem.WINDOWS) { this.updateTickTimer.scheduleAtFixedRate(0, 3000) { - val currentNumPublicNetworks = enumerateNetworks()?.filter { net -> + publicNetworksLocal = enumerateNetworks()?.filter { net -> net.connected == true && net.category == NetworkCategory.PUBLIC } ?: listOf() - val currentNumPublicNetworksCount = currentNumPublicNetworks.count() - - if (numPublicNetworks != currentNumPublicNetworksCount) { - numPublicNetworks = currentNumPublicNetworksCount - if (lastPublicNetworkStatus != 0u) { - server.statusSystem.removeStatus(lastPublicNetworkStatus) - lastPublicNetworkStatus = 0u - } - - if (lastPublicNetworkStatus == 0u && numPublicNetworks > 0) { - lastPublicNetworkStatus = server.statusSystem.addStatus( - StatusDataUnion().apply { - type = StatusData.StatusPublicNetwork - value = StatusPublicNetworkT().apply { - adapters = currentNumPublicNetworks.map { it.name }.toTypedArray() - } - }, - false, - ) - } - } } } } diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/Main.kt b/server/desktop/src/main/java/dev/slimevr/desktop/Main.kt index 4e05dd316..22267c260 100644 --- a/server/desktop/src/main/java/dev/slimevr/desktop/Main.kt +++ b/server/desktop/src/main/java/dev/slimevr/desktop/Main.kt @@ -129,12 +129,11 @@ fun main(args: Array) { { _ -> DesktopSerialHandler() }, { _ -> DesktopSerialFlashingHandler() }, { _ -> DesktopVRCConfigHandler() }, + { server -> DesktopNetworkProfileChecker(server) }, configManager = configManager, ) vrServer.start() - NetworkProfileChecker(vrServer) - // Start service for USB HID trackers DesktopHIDManager( "Sensors HID service", diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/platform/SteamVRBridge.kt b/server/desktop/src/main/java/dev/slimevr/desktop/platform/SteamVRBridge.kt index 863e17931..8a369ba34 100644 --- a/server/desktop/src/main/java/dev/slimevr/desktop/platform/SteamVRBridge.kt +++ b/server/desktop/src/main/java/dev/slimevr/desktop/platform/SteamVRBridge.kt @@ -15,20 +15,18 @@ import dev.slimevr.tracking.trackers.TrackerRole.Companion.getById import dev.slimevr.tracking.trackers.TrackerUtils.getTrackerForSkeleton import dev.slimevr.util.ann.VRServerThread import io.eiren.util.collections.FastList -import solarxr_protocol.rpc.StatusData -import solarxr_protocol.rpc.StatusDataUnion -import solarxr_protocol.rpc.StatusSteamVRDisconnectedT abstract class SteamVRBridge( protected val server: VRServer, threadName: String, bridgeName: String, - protected val bridgeSettingsKey: String, + val bridgeSettingsKey: String, protected val shareableTrackers: List, ) : ProtobufBridge(bridgeName), Runnable { protected val runnerThread: Thread = Thread(this, threadName) protected val config: BridgeConfig = server.configManager.vrConfig.getBridge(bridgeSettingsKey) + var connected: Boolean = false @VRServerThread override fun startBridge() { @@ -187,7 +185,7 @@ abstract class SteamVRBridge( hasRotation = true, userEditable = true, isComputed = true, - needsReset = true, + allowReset = true, isHmd = isHmd, ) @@ -428,33 +426,13 @@ abstract class SteamVRBridge( } } - /** - * When 0, then it means null - */ - protected var lastSteamVRStatus: Int = 0 - @BridgeThread protected fun reportDisconnected() { - if (lastSteamVRStatus != 0) { - return - } - val statusData = StatusSteamVRDisconnectedT() - statusData.bridgeSettingsName = bridgeSettingsKey - - val status = StatusDataUnion() - status.type = StatusData.StatusSteamVRDisconnected - status.value = statusData - lastSteamVRStatus = instance.statusSystem - .addStatus(status, false).toInt() + connected = false } @BridgeThread protected fun reportConnected() { - if (lastSteamVRStatus == 0) { - return - } - instance.statusSystem - .removeStatus(lastSteamVRStatus.toUInt()) - lastSteamVRStatus = 0 + connected = true } } diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/platform/linux/UnixSocketBridge.java b/server/desktop/src/main/java/dev/slimevr/desktop/platform/linux/UnixSocketBridge.java index d9b49bff3..1c29cedd2 100644 --- a/server/desktop/src/main/java/dev/slimevr/desktop/platform/linux/UnixSocketBridge.java +++ b/server/desktop/src/main/java/dev/slimevr/desktop/platform/linux/UnixSocketBridge.java @@ -8,6 +8,7 @@ import dev.slimevr.desktop.platform.SteamVRBridge; import dev.slimevr.tracking.trackers.Tracker; import io.eiren.util.ann.ThreadSafe; import io.eiren.util.logging.LogManager; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; @@ -25,6 +26,7 @@ import java.util.List; public class UnixSocketBridge extends SteamVRBridge implements AutoCloseable { public final String socketPath; public final UnixDomainSocketAddress socketAddress; + private final String bridgeSettingsKey; private final ByteBuffer dst = ByteBuffer.allocate(2048).order(ByteOrder.LITTLE_ENDIAN); private final ByteBuffer src = ByteBuffer.allocate(2048).order(ByteOrder.LITTLE_ENDIAN); @@ -41,6 +43,7 @@ public class UnixSocketBridge extends SteamVRBridge implements AutoCloseable { List shareableTrackers ) { super(server, "Named socket thread", bridgeName, bridgeSettingsKey, shareableTrackers); + this.bridgeSettingsKey = bridgeSettingsKey; this.socketPath = socketPath; this.socketAddress = UnixDomainSocketAddress.of(socketPath); @@ -250,5 +253,11 @@ public class UnixSocketBridge extends SteamVRBridge implements AutoCloseable { public boolean isConnected() { return channel != null && channel.isConnected(); } + + @NotNull + @Override + public String getBridgeConfigKey() { + return bridgeSettingsKey; + } } diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsNamedPipeBridge.java b/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsNamedPipeBridge.java index ab577eb9a..5099fccd8 100644 --- a/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsNamedPipeBridge.java +++ b/server/desktop/src/main/java/dev/slimevr/desktop/platform/windows/WindowsNamedPipeBridge.java @@ -14,6 +14,7 @@ import dev.slimevr.desktop.platform.SteamVRBridge; import dev.slimevr.tracking.trackers.Tracker; import io.eiren.util.ann.ThreadSafe; import io.eiren.util.logging.LogManager; +import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.List; @@ -37,6 +38,7 @@ public class WindowsNamedPipeBridge extends SteamVRBridge { private static final Advapi32 adv32 = Advapi32.INSTANCE; protected final String pipeName; + protected final String bridgeSettingsKey; private final byte[] buffArray = new byte[2048]; protected WindowsPipe pipe; protected WinNT.HANDLE openEvent = k32.CreateEvent(null, false, false, null); @@ -63,6 +65,7 @@ public class WindowsNamedPipeBridge extends SteamVRBridge { ) { super(server, "Named pipe thread", bridgeName, bridgeSettingsKey, shareableTrackers); this.pipeName = pipeName; + this.bridgeSettingsKey = bridgeSettingsKey; overlappedWait.hEvent = rxEvent; } @@ -321,4 +324,10 @@ public class WindowsNamedPipeBridge extends SteamVRBridge { public boolean isConnected() { return pipe != null && pipe.state == PipeState.OPEN; } + + @NotNull + @Override + public String getBridgeConfigKey() { + return this.bridgeSettingsKey; + } } diff --git a/solarxr-protocol b/solarxr-protocol index 2fe6f9cf8..024c28cad 160000 --- a/solarxr-protocol +++ b/solarxr-protocol @@ -1 +1 @@ -Subproject commit 2fe6f9cf8d775174336e559b3bc8e948f3da12c2 +Subproject commit 024c28cadbe03002c66723f7e3ce8bf655392f0f