From c43da8c01dd00f8f8477d8fe7957ae9f2ec40b41 Mon Sep 17 00:00:00 2001 From: ABLA Date: Mon, 16 Mar 2026 00:31:50 +0300 Subject: [PATCH 01/24] feat: custom model paths + auto-download for missing models - gui_bridge.py: read `models_path` from qubes-config.json to let users store models on any drive/path (e.g. D:\MyModels). Priority order: QUBES_MODELS_DIR env var > qubes-config.json models_path > auto-detect. Sets QUBES_MODELS_DIR so all subsystems see the same base path. - audio/audio_manager.py: _load_config_from_env now resolves all model paths (piper, whisper, qwen3-tts) relative to QUBES_MODELS_DIR instead of the hardcoded ~/.qubes/models default. - audio/audio_manager.py: _get_qwen3_provider now auto-triggers Qwen3ModelDownloader.start_download() when the selected variant is not yet on disk, so the model downloads automatically in the background instead of silently failing. Co-Authored-By: Claude Sonnet 4.6 --- audio/audio_manager.py | 31 ++++++++++++++++++++---- gui_bridge.py | 54 +++++++++++++++++++++++++++++++++++------- 2 files changed, 72 insertions(+), 13 deletions(-) diff --git a/audio/audio_manager.py b/audio/audio_manager.py index a7f85d2..72073cb 100644 --- a/audio/audio_manager.py +++ b/audio/audio_manager.py @@ -207,6 +207,8 @@ def __init__(self, config: Optional[Dict[str, Any]] = None, qube_data_dir: Optio def _load_config_from_env(self) -> Dict[str, Any]: """Load configuration from environment variables""" + # Resolve base models dir: QUBES_MODELS_DIR env var or platform default + _models_base = Path(os.getenv("QUBES_MODELS_DIR", "~/.qubes/models")).expanduser() return { "openai_api_key": os.getenv("OPENAI_API_KEY"), "elevenlabs_api_key": os.getenv("ELEVENLABS_API_KEY"), @@ -216,15 +218,16 @@ def _load_config_from_env(self) -> Dict[str, Any]: "piper_model_path": Path( os.getenv( "PIPER_MODEL_PATH", - "~/.qubes/models/piper/en_US-lessac-medium.onnx" + str(_models_base / "piper" / "en_US-lessac-medium.onnx") ) ), "whisper_cpp_model_path": Path( os.getenv( "WHISPER_MODEL_PATH", - "~/.qubes/models/whisper/ggml-base.en.bin" + str(_models_base / "whisper" / "ggml-base.en.bin") ) ), + "qwen3_models_dir": _models_base / "qwen3-tts", } def _init_tts_providers(self): @@ -384,9 +387,28 @@ def _get_qwen3_provider(self) -> Optional[TTSProvider]: except Exception: pass # Use defaults + # Use QUBES_MODELS_DIR if set, otherwise platform default + qwen3_models_dir = self.config.get("qwen3_models_dir") + + # Auto-download model if not present + if qwen3_models_dir is not None: + try: + from audio.model_downloader import Qwen3ModelDownloader + _dl = Qwen3ModelDownloader(models_dir=qwen3_models_dir) + _variant_key = f"{model_variant}-Base" + if not _dl.is_model_downloaded(_variant_key): + logger.info("qwen3_auto_download_triggered", variant=_variant_key) + _dl.start_download(_variant_key) + # Also download tokenizer + if not _dl.is_model_downloaded("Tokenizer"): + _dl.start_download("Tokenizer") + except Exception as _dl_err: + logger.warning("qwen3_auto_download_failed", error=str(_dl_err)) + provider = Qwen3TTSProvider( model_variant=model_variant, - use_flash_attention=use_flash_attention + use_flash_attention=use_flash_attention, + **({"models_dir": qwen3_models_dir} if qwen3_models_dir else {}) ) self.tts_providers["qwen3"] = provider logger.info("tts_provider_initialized", provider="qwen3") @@ -489,7 +511,8 @@ def check_qwen3_status(self) -> Dict[str, Any]: result["available"] = result["recommended_variant"] is not None # Check downloaded models - models_dir = Path.home() / ".qubes" / "models" / "qwen3-tts" + _models_base = Path(os.getenv("QUBES_MODELS_DIR", str(Path.home() / ".qubes" / "models"))) + models_dir = _models_base / "qwen3-tts" if models_dir.exists(): for model_dir in models_dir.iterdir(): if model_dir.is_dir(): diff --git a/gui_bridge.py b/gui_bridge.py index 254a70a..54b6ffa 100644 --- a/gui_bridge.py +++ b/gui_bridge.py @@ -63,18 +63,54 @@ def _quiet_excepthook(exc_type, exc_value, exc_tb): from dotenv import load_dotenv load_dotenv(Path(__file__).parent / ".env") +# ============================================================================ +# CUSTOM MODELS PATH (qubes-config.json or QUBES_MODELS_DIR env var) +# ============================================================================ +# Allow users to store models on any drive/path they want. +# Priority: QUBES_MODELS_DIR env var > qubes-config.json models_path > auto-detect +_custom_models_dir = None + +# 1. Check env var (set by .env file or OS) +if os.environ.get('QUBES_MODELS_DIR'): + _custom_models_dir = Path(os.environ['QUBES_MODELS_DIR']) + +# 2. Check qubes-config.json next to the exe +if _custom_models_dir is None: + try: + import json as _json + _exe_dir_cfg = Path(sys.executable).parent + for _cfg_path in [ + _exe_dir_cfg.parent / "qubes-config.json", + _exe_dir_cfg / "qubes-config.json", + Path(__file__).parent.parent / "qubes-config.json", + ]: + if _cfg_path.exists(): + _cfg = _json.loads(_cfg_path.read_text(encoding="utf-8")) + if _cfg.get("models_path"): + _custom_models_dir = Path(_cfg["models_path"]) + break + except Exception: + pass + # Auto-detect bundled HuggingFace models (heavy bundle) # Sets HF_HOME so kokoro, sentence-transformers find pre-downloaded models -if not os.environ.get('HF_HOME') and getattr(sys, 'frozen', False): - _exe_dir = Path(sys.executable).parent - # --onedir layout: exe is at Qubes/qubes-backend/qubes-backend - # models are at Qubes/models/huggingface (one level up) - _hf_models = _exe_dir.parent / "models" / "huggingface" - if not _hf_models.exists(): - # Flat layout: models next to exe - _hf_models = _exe_dir / "models" / "huggingface" - if _hf_models.exists(): +if not os.environ.get('HF_HOME'): + if _custom_models_dir is not None: + _hf_models = _custom_models_dir / "huggingface" + _hf_models.mkdir(parents=True, exist_ok=True) os.environ['HF_HOME'] = str(_hf_models) + os.environ.setdefault('QUBES_MODELS_DIR', str(_custom_models_dir)) + elif getattr(sys, 'frozen', False): + _exe_dir = Path(sys.executable).parent + # --onedir layout: exe is at Qubes/qubes-backend/qubes-backend + # models are at Qubes/models/huggingface (one level up) + _hf_models = _exe_dir.parent / "models" / "huggingface" + if not _hf_models.exists(): + # Flat layout: models next to exe + _hf_models = _exe_dir / "models" / "huggingface" + if _hf_models.exists(): + os.environ['HF_HOME'] = str(_hf_models) + os.environ.setdefault('QUBES_MODELS_DIR', str(_hf_models.parent)) # CRITICAL: Disable all logging to stdout/stderr before importing anything # Set environment variable to disable structlog output From 8a964c4af001e963b68d8eead6cf085e0fe7e004 Mon Sep 17 00:00:00 2001 From: ABLA Date: Mon, 16 Mar 2026 17:25:23 +0300 Subject: [PATCH 02/24] feat: add Local Models section in Settings for updating Ollama and TTS models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add pull_ollama_model Rust command: streams NDJSON progress from Ollama API, emits ollama-pull-progress Tauri events to frontend - Add check_local_tts_models Rust command: delegates to Python sidecar - Add update_local_tts_models Rust command: delegates to Python sidecar - Add check-local-tts-models Python handler: checks HF_HOME for Kokoro, sentence-transformers, and Whisper model directories - Add update-local-tts-models Python handler: re-pulls models from HuggingFace using snapshot_download with QUBES_MODELS_DIR awareness - Add 📦 Local Models collapsible section in SettingsTab after GPU Acceleration: - Lists installed Ollama models with per-model Update button - Update All Ollama Models button with live progress bar - Shows Kokoro TTS, Sentence Transformers, Whisper install status - Re-download Voice Models button Co-Authored-By: Claude Sonnet 4.6 --- gui_bridge.py | 67 +++++ qubes-gui/src-tauri/src/lib.rs | 84 +++++++ qubes-gui/src/components/tabs/SettingsTab.tsx | 234 +++++++++++++++++- 3 files changed, 384 insertions(+), 1 deletion(-) diff --git a/gui_bridge.py b/gui_bridge.py index 54b6ffa..36d61da 100644 --- a/gui_bridge.py +++ b/gui_bridge.py @@ -12598,6 +12598,73 @@ def debug_log(msg): result = await user_bridge.uninstall_gpu_acceleration(user_id) print(json.dumps(result)) + elif command == "check-local-tts-models": + if len(sys.argv) < 3: + print(json.dumps({"error": "User ID required"}), file=sys.stderr) + sys.exit(1) + + hf_home = os.environ.get("HF_HOME", "") + models_dir = os.environ.get("QUBES_MODELS_DIR", "") + + kokoro_installed = False + st_installed = False + whisper_installed = False + + if hf_home: + hf_path = Path(hf_home) + kokoro_path = hf_path / "hub" / "models--hexgrad--Kokoro-82M" + st_path = hf_path / "hub" / "models--sentence-transformers--all-MiniLM-L6-v2" + kokoro_installed = kokoro_path.exists() + st_installed = st_path.exists() + + if models_dir: + whisper_path = Path(models_dir) / "whisper" + whisper_installed = any(whisper_path.glob("*.bin")) if whisper_path.exists() else False + + print(json.dumps({ + "kokoro_installed": kokoro_installed, + "sentence_transformers_installed": st_installed, + "whisper_installed": whisper_installed, + "hf_home": hf_home, + "models_dir": str(models_dir or hf_home), + })) + + elif command == "update-local-tts-models": + if len(sys.argv) < 3: + print(json.dumps({"error": "User ID required"}), file=sys.stderr) + sys.exit(1) + + updated = [] + errors = [] + + try: + from huggingface_hub import snapshot_download + hf_home = os.environ.get("HF_HOME", None) + dl_kwargs = {} + if hf_home: + dl_kwargs["cache_dir"] = str(Path(hf_home) / "hub") + + try: + snapshot_download("hexgrad/Kokoro-82M", local_files_only=False, **dl_kwargs) + updated.append("kokoro-82m") + except Exception as e: + errors.append(f"Kokoro: {str(e)}") + + try: + snapshot_download("sentence-transformers/all-MiniLM-L6-v2", local_files_only=False, **dl_kwargs) + updated.append("sentence-transformers") + except Exception as e: + errors.append(f"sentence-transformers: {str(e)}") + + except ImportError as e: + errors.append(f"huggingface_hub not available: {str(e)}") + + print(json.dumps({ + "success": len(errors) == 0, + "updated": updated, + "errors": errors, + })) + elif command == "update-qwen3-preferences": if len(sys.argv) < 3: print(json.dumps({"error": "User ID required"}), file=sys.stderr) diff --git a/qubes-gui/src-tauri/src/lib.rs b/qubes-gui/src-tauri/src/lib.rs index 7795f9f..697dee6 100644 --- a/qubes-gui/src-tauri/src/lib.rs +++ b/qubes-gui/src-tauri/src/lib.rs @@ -5901,6 +5901,87 @@ async fn start_ollama() -> Result { Ok(true) } +/// Pull an Ollama model, streaming progress events to the frontend +#[tauri::command] +async fn pull_ollama_model(app_handle: AppHandle, model_name: String) -> Result { + use futures_util::StreamExt; + + let client = reqwest::Client::new(); + let body = serde_json::json!({ "name": model_name, "stream": true }); + + let response = client + .post("http://127.0.0.1:11434/api/pull") + .json(&body) + .send() + .await + .map_err(|e| format!("Failed to connect to Ollama: {}", e))?; + + if !response.status().is_success() { + return Err(format!("Ollama pull failed with status: {}", response.status())); + } + + let mut stream = response.bytes_stream(); + let mut buf = String::new(); + + while let Some(chunk) = stream.next().await { + let bytes = chunk.map_err(|e| format!("Stream error: {}", e))?; + buf.push_str(&String::from_utf8_lossy(&bytes)); + + // Process complete NDJSON lines + while let Some(pos) = buf.find('\n') { + let line = buf[..pos].trim().to_string(); + buf = buf[pos + 1..].to_string(); + + if line.is_empty() { + continue; + } + + if let Ok(json) = serde_json::from_str::(&line) { + let payload = serde_json::json!({ + "model": model_name, + "status": json.get("status").and_then(|v| v.as_str()).unwrap_or(""), + "completed": json.get("completed").and_then(|v| v.as_u64()), + "total": json.get("total").and_then(|v| v.as_u64()), + "digest": json.get("digest").and_then(|v| v.as_str()).unwrap_or(""), + }); + let _ = app_handle.emit("ollama-pull-progress", payload); + } + } + } + + // Emit done event + let _ = app_handle.emit("ollama-pull-progress", serde_json::json!({ + "model": model_name, + "status": "done", + "completed": null, + "total": null, + })); + + Ok(true) +} + +/// Check status of local TTS/embedding models via Python sidecar +#[tauri::command] +async fn check_local_tts_models(app_handle: AppHandle, user_id: String) -> Result { + let args = vec![user_id]; + let secrets = HashMap::new(); + let result = sidecar_execute_with_retry("check-local-tts-models", args, secrets, Some(&app_handle), None).await?; + let response: serde_json::Value = serde_json::from_value(result) + .map_err(|e| format!("Failed to parse check-local-tts-models response: {}", e))?; + Ok(response) +} + +/// Re-download/update local TTS and embedding models via Python sidecar +#[tauri::command] +async fn update_local_tts_models(app_handle: AppHandle, user_id: String) -> Result { + let args = vec![user_id]; + let secrets = HashMap::new(); + let result = sidecar_execute_with_retry("update-local-tts-models", args, secrets, Some(&app_handle), None).await?; + let response: serde_json::Value = serde_json::from_value(result) + .map_err(|e| format!("Failed to parse update-local-tts-models response: {}", e))?; + Ok(response) +} + /// Open external URL in default browser (using safe opener plugin) #[tauri::command] async fn open_external_url(url: String) -> Result { @@ -7824,6 +7905,9 @@ pub fn run() { check_ollama_status, get_backend_diagnostics, start_ollama, + pull_ollama_model, + check_local_tts_models, + update_local_tts_models, open_external_url, // Chain Sync (NFT-Bundled Storage) sync_to_chain, diff --git a/qubes-gui/src/components/tabs/SettingsTab.tsx b/qubes-gui/src/components/tabs/SettingsTab.tsx index 39bd518..82ab78f 100644 --- a/qubes-gui/src/components/tabs/SettingsTab.tsx +++ b/qubes-gui/src/components/tabs/SettingsTab.tsx @@ -1,5 +1,6 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { invoke } from '@tauri-apps/api/core'; +import { listen } from '@tauri-apps/api/event'; import { GlassCard, GlassButton, GlassInput } from '../glass'; import { useAuth } from '../../hooks/useAuth'; import { useChainState } from '../../contexts/ChainStateContext'; @@ -219,6 +220,7 @@ export const SettingsTab: React.FC = () => { trustPersonality: true, voiceSettings: true, gpuAcceleration: true, + localModels: true, decisionIntelligence: true, security: true, celebrationSettings: true, @@ -296,6 +298,20 @@ export const SettingsTab: React.FC = () => { const [gpuUninstalling, setGpuUninstalling] = useState(false); const [gpuError, setGpuError] = useState(null); + // Local Models state + const [ollamaModels, setOllamaModels] = useState([]); + const [isPullingModel, setIsPullingModel] = useState(null); + const [pullProgress, setPullProgress] = useState<{ status: string; completed?: number; total?: number } | null>(null); + const [ttsModelStatus, setTtsModelStatus] = useState<{ + kokoro_installed: boolean; + sentence_transformers_installed: boolean; + whisper_installed: boolean; + models_dir: string; + } | null>(null); + const [isUpdatingTts, setIsUpdatingTts] = useState(false); + const [ttsUpdateResult, setTtsUpdateResult] = useState(null); + const pullListenerRef = useRef<(() => void) | null>(null); + const formatBytes = (bytes: number): string => { if (bytes === 0) return '0 B'; const k = 1024; @@ -379,6 +395,93 @@ export const SettingsTab: React.FC = () => { } }, [userId]); + // Listen for Ollama pull progress events + useEffect(() => { + let unlisten: (() => void) | null = null; + listen<{ model: string; status: string; completed?: number; total?: number }>('ollama-pull-progress', (event) => { + if (event.payload.status === 'done') { + setPullProgress(null); + setIsPullingModel(null); + // Refresh model list after pull + checkOllamaModels(); + } else { + setPullProgress({ status: event.payload.status, completed: event.payload.completed, total: event.payload.total }); + } + }).then((fn) => { + unlisten = fn; + pullListenerRef.current = fn; + }); + return () => { + if (unlisten) unlisten(); + }; + }, []); + + const checkOllamaModels = async () => { + try { + const result = await invoke<{ running: boolean; models: string[] }>('check_ollama_status'); + if (result.running) { + setOllamaModels(result.models); + } + } catch (err) { + console.error('Failed to check Ollama models:', err); + } + }; + + const checkTtsModels = async () => { + if (!userId) return; + try { + const result = await invoke('check_local_tts_models', { userId }); + setTtsModelStatus(result); + } catch (err) { + console.error('Failed to check TTS models:', err); + } + }; + + const handlePullModel = async (modelName: string) => { + if (isPullingModel) return; + setIsPullingModel(modelName); + setPullProgress({ status: 'starting...' }); + try { + await invoke('pull_ollama_model', { modelName }); + } catch (err) { + setIsPullingModel(null); + setPullProgress(null); + alert(`Failed to pull ${modelName}: ${err}`); + } + }; + + const handleUpdateAllOllamaModels = async () => { + if (isPullingModel || ollamaModels.length === 0) return; + for (const model of ollamaModels) { + setIsPullingModel(model); + setPullProgress({ status: 'starting...' }); + try { + await invoke('pull_ollama_model', { modelName: model }); + } catch (err) { + console.error(`Failed to pull ${model}:`, err); + } + } + }; + + const handleUpdateTtsModels = async () => { + if (!userId || isUpdatingTts) return; + setIsUpdatingTts(true); + setTtsUpdateResult(null); + try { + const result = await invoke<{ success: boolean; updated: string[]; errors: string[] }>('update_local_tts_models', { userId }); + if (result.success) { + setTtsUpdateResult(`Updated: ${result.updated.join(', ') || 'already up to date'}`); + } else { + setTtsUpdateResult(`Errors: ${result.errors.join('; ')}`); + } + await checkTtsModels(); + } catch (err) { + setTtsUpdateResult(`Failed: ${err}`); + } finally { + setIsUpdatingTts(false); + } + }; + const checkGpuAcceleration = async () => { try { const result = await invoke('check_gpu_acceleration', { userId }); @@ -1775,6 +1878,135 @@ export const SettingsTab: React.FC = () => { )} + + {/* Local Models */} + + + + {!collapsedPanels.localModels && ( + <> +

+ Update AI models (Ollama) and voice/embedding models (Kokoro TTS, Sentence Transformers). +

+ + {/* Ollama Models */} +
+

🤖 AI Models (Ollama)

+ {ollamaModels.length > 0 ? ( +
+ {ollamaModels.map((model) => ( +
+
+ {model} + ✓ installed +
+ handlePullModel(model)} + disabled={isPullingModel !== null} + > + {isPullingModel === model ? 'Updating...' : 'Update'} + +
+ ))} + + {isPullingModel ? `Updating ${isPullingModel}...` : 'Update All Ollama Models'} + +
+ ) : ( +
No Ollama models installed yet.
+ )} + + {/* Pull progress */} + {pullProgress && ( +
+
+ {pullProgress.status} + {pullProgress.total && pullProgress.total > 0 && ( + + {formatBytes(pullProgress.completed ?? 0)} / {formatBytes(pullProgress.total)} + + )} +
+ {pullProgress.total && pullProgress.total > 0 && ( +
+
+
+ )} +
+ )} +
+ + {/* Voice & Embedding Models */} +
+

🎙️ Voice & Embedding Models

+ {ttsModelStatus ? ( +
+
+ Kokoro TTS 82M + + {ttsModelStatus.kokoro_installed ? '✓ installed' : '✗ missing'} + +
+
+ Sentence Transformers + + {ttsModelStatus.sentence_transformers_installed ? '✓ installed' : '✗ missing'} + +
+
+ Whisper STT + + {ttsModelStatus.whisper_installed ? '✓ installed' : '✗ missing'} + +
+
+ ) : ( +
Click to check model status...
+ )} + + + {isUpdatingTts ? 'Re-downloading...' : 'Re-download Voice Models'} + + + {ttsUpdateResult && ( +

+ {ttsUpdateResult} +

+ )} +
+ + )} +
{/* Right Column */} From 7d691a0182078c53aa97d6875fd69cf7f6b65775 Mon Sep 17 00:00:00 2001 From: ABLA Date: Mon, 16 Mar 2026 17:54:18 +0300 Subject: [PATCH 03/24] feat: balance widget in QubeManager + sweep on card flip + TTS fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Balance UI: - Remove TotalBalanceWidget from header title bar - Add Total Balance widget in QubeManagerTab toolbar (next to + New Qube): - Shows total BCH across all qubes (gray when 0, accent when has balance) - Refresh button (↻) refreshes all qube balances - Sweep All button (↓ Sweep) — disabled/gray at 0, active when balance > 0 - Inline sweep-all input: enter destination address, confirm, sweeps all qubes with balance Card Flip Sweep: - Add ↓ Sweep Funds button on blockchain flip side per qube - Gray + disabled when balance = 0 - Active (accent color) when balance > 0 - Inline input: enter bitcoincash:q... address, confirm, sweeps that qube only - Shows ✓/✗ result, updates cached balance to 0 on success Backend: - Add sweep-qube-wallet Python command (calls orchestrator._sweep_before_delete) - Add sweep_qube_wallet Rust invoke command TTS Fix: - Re-applied to local D:\Qubes binary (torch/nn/__init__.py pre-imports functional+init before modules) Co-Authored-By: Claude Sonnet 4.6 --- gui_bridge.py | 14 ++ qubes-gui/src-tauri/src/lib.rs | 12 ++ qubes-gui/src/App.tsx | 1 - .../src/components/tabs/QubeManagerTab.tsx | 187 ++++++++++++++++++ 4 files changed, 213 insertions(+), 1 deletion(-) diff --git a/gui_bridge.py b/gui_bridge.py index b497dac..38c9bb3 100644 --- a/gui_bridge.py +++ b/gui_bridge.py @@ -12619,6 +12619,20 @@ def debug_log(msg): result = await user_bridge.uninstall_gpu_acceleration(user_id) print(json.dumps(result)) + elif command == "sweep-qube-wallet": + if len(sys.argv) < 5: + print(json.dumps({"error": "user_id, qube_id, sweep_address required"}), file=sys.stderr) + sys.exit(1) + user_id = sys.argv[2] + qube_id = sys.argv[3] + sweep_address = sys.argv[4] + password = sys.argv[5] if len(sys.argv) > 5 else None + user_bridge = GUIBridge(user_id=user_id) + if password: + user_bridge.orchestrator.set_master_key(password) + swept_sats = await user_bridge.orchestrator._sweep_before_delete(qube_id, password, sweep_address) + print(json.dumps({"success": True, "swept_sats": swept_sats or 0})) + elif command == "check-local-tts-models": if len(sys.argv) < 3: print(json.dumps({"error": "User ID required"}), file=sys.stderr) diff --git a/qubes-gui/src-tauri/src/lib.rs b/qubes-gui/src-tauri/src/lib.rs index 697dee6..70d147a 100644 --- a/qubes-gui/src-tauri/src/lib.rs +++ b/qubes-gui/src-tauri/src/lib.rs @@ -5901,6 +5901,17 @@ async fn start_ollama() -> Result { Ok(true) } +/// Sweep all BCH from a single Qube's wallet to a target address (without deleting the Qube) +#[tauri::command] +async fn sweep_qube_wallet(app_handle: AppHandle, user_id: String, qube_id: String, sweep_address: String, password: String) -> Result { + let args = vec![user_id, qube_id, sweep_address, password]; + let secrets = HashMap::new(); + let result = sidecar_execute_with_retry("sweep-qube-wallet", args, secrets, Some(&app_handle), None).await?; + let response: serde_json::Value = serde_json::from_value(result) + .map_err(|e| format!("Failed to parse sweep-qube-wallet response: {}", e))?; + Ok(response) +} + /// Pull an Ollama model, streaming progress events to the frontend #[tauri::command] async fn pull_ollama_model(app_handle: AppHandle, model_name: String) -> Result { @@ -7905,6 +7916,7 @@ pub fn run() { check_ollama_status, get_backend_diagnostics, start_ollama, + sweep_qube_wallet, pull_ollama_model, check_local_tts_models, update_local_tts_models, diff --git a/qubes-gui/src/App.tsx b/qubes-gui/src/App.tsx index 82adc0d..774c592 100644 --- a/qubes-gui/src/App.tsx +++ b/qubes-gui/src/App.tsx @@ -548,7 +548,6 @@ function App() { {/* Title Bar */}
QUBES ({qubes.length} loaded) -
👤 diff --git a/qubes-gui/src/components/tabs/QubeManagerTab.tsx b/qubes-gui/src/components/tabs/QubeManagerTab.tsx index ae1351c..63ddc6c 100644 --- a/qubes-gui/src/components/tabs/QubeManagerTab.tsx +++ b/qubes-gui/src/components/tabs/QubeManagerTab.tsx @@ -135,6 +135,57 @@ export const QubeManagerTab: React.FC = ({ // Pinata configuration check const [pinataConfigured, setPinataConfigured] = useState(null); + + // Total balance widget state + const { wallets: walletCache, setBalance: setCachedBalanceGlobal } = useWalletCache(); + const [isRefreshingAll, setIsRefreshingAll] = useState(false); + const [showSweepAllModal, setShowSweepAllModal] = useState(false); + const [sweepAllAddress, setSweepAllAddress] = useState(''); + const [isSweepingAll, setIsSweepingAll] = useState(false); + const [sweepAllResult, setSweepAllResult] = useState(null); + + const totalSats = qubes.reduce((sum, q) => sum + (walletCache[q.qube_id]?.balance ?? 0), 0); + const hasAnyBalance = qubes.some((q) => (walletCache[q.qube_id]?.balance ?? 0) > 0); + + const handleRefreshAllBalances = async () => { + if (isRefreshingAll || !userId || !masterPassword) return; + setIsRefreshingAll(true); + try { + await Promise.all( + qubes.filter((q) => q.wallet_address).map(async (q) => { + try { + const result = await invoke<{ success?: boolean; balance_sats?: number }>('get_wallet_info', { + userId, qubeId: q.qube_id, password: masterPassword, + }); + if (result.balance_sats !== undefined) setCachedBalanceGlobal(q.qube_id, result.balance_sats); + } catch { /* ignore per-qube errors */ } + }) + ); + } finally { + setIsRefreshingAll(false); + } + }; + + const handleSweepAll = async () => { + if (!sweepAllAddress || !userId || !masterPassword) return; + setIsSweepingAll(true); + setSweepAllResult(null); + let totalSwept = 0; + const errors: string[] = []; + for (const q of qubes.filter((q) => q.wallet_address && (walletCache[q.qube_id]?.balance ?? 0) > 0)) { + try { + const result = await invoke<{ success: boolean; swept_sats: number; error?: string }>('sweep_qube_wallet', { + userId, qubeId: q.qube_id, sweepAddress: sweepAllAddress, password: masterPassword, + }); + if (result.success) { totalSwept += result.swept_sats; setCachedBalanceGlobal(q.qube_id, 0); } + else errors.push(`${q.name}: ${result.error}`); + } catch (err) { errors.push(`${q.name}: ${String(err)}`); } + } + setSweepAllResult(errors.length === 0 + ? `✓ Swept ${(totalSwept / 1e8).toFixed(8)} BCH from ${qubes.length} qubes` + : `Swept ${(totalSwept / 1e8).toFixed(8)} BCH. Errors: ${errors.join('; ')}`); + setIsSweepingAll(false); + }; const [walletQubes, setWalletQubes] = useState = ({ > Restore + {/* Total Balance Widget */} +
+
+ Total Balance + + {isRefreshingAll ? '...' : `${(totalSats / 1e8).toFixed(8)} BCH`} + +
+ + {!showSweepAllModal ? ( + + ) : ( +
+ setSweepAllAddress(e.target.value)} + placeholder="bitcoincash:q..." + className="bg-transparent text-[10px] text-text-primary placeholder:text-text-disabled font-mono focus:outline-none w-40" + autoFocus + /> + + +
+ )} +
+ {sweepAllResult && ( +
+ {sweepAllResult} +
+ )} + + New Qube @@ -1986,6 +2096,38 @@ const QubeCard: React.FC = ({ qube, allQubes, onEdit, onDelete, o const [walletBalance, setWalletBalance] = useState(cachedWalletData?.balance ?? null); // P2SH wallet const [walletBalanceLoading, setWalletBalanceLoading] = useState(false); const [walletBalanceError, setWalletBalanceError] = useState(cachedWalletData?.error ?? null); + const [showSweepModal, setShowSweepModal] = useState(false); + const [sweepAddress, setSweepAddress] = useState(''); + const [sweepPassword, setSweepPassword] = useState(''); + const [isSweeping, setIsSweeping] = useState(false); + const [sweepResult, setSweepResult] = useState(null); + + const handleSweep = async () => { + if (!sweepAddress || !userId || !masterPassword) return; + setIsSweeping(true); + setSweepResult(null); + try { + const result = await invoke<{ success: boolean; swept_sats: number; error?: string }>('sweep_qube_wallet', { + userId, + qubeId: qube.qube_id, + sweepAddress, + password: sweepPassword || masterPassword, + }); + if (result.success) { + const bch = (result.swept_sats / 1e8).toFixed(8); + setSweepResult(`✓ Swept ${bch} BCH`); + setWalletBalance(0); + setCachedBalance(qube.qube_id, 0); + setSweepAddress(''); + } else { + setSweepResult(`✗ ${result.error || 'Sweep failed'}`); + } + } catch (err) { + setSweepResult(`✗ ${String(err)}`); + } finally { + setIsSweeping(false); + } + }; // Custom voices now come from VoiceLibraryContext (see useVoiceLibrary hook above) @@ -3372,6 +3514,51 @@ const QubeCard: React.FC = ({ qube, allQubes, onEdit, onDelete, o
)}
+ + {/* Sweep button — active only when balance > 0 */} + {!showSweepModal ? ( + + ) : ( +
+ setSweepAddress(e.target.value)} + placeholder="bitcoincash:q..." + className="w-full bg-bg-primary border border-glass-border rounded px-2 py-1 text-text-primary placeholder:text-text-disabled font-mono text-[10px] mb-1 focus:outline-none" + /> +
+ + +
+ {sweepResult && ( +

+ {sweepResult} +

+ )} +
+ )}
)} From ad5cf43ca95d437a3d21bdc7c728b2a145693bf2 Mon Sep 17 00:00:00 2001 From: ABLA Date: Mon, 16 Mar 2026 21:09:55 +0300 Subject: [PATCH 04/24] build: update Tauri crate versions and add pnpm lockfile for local builds - Loosen Tauri crate version constraints to "2" so cargo resolves latest compatible versions (fixes version mismatch with npm packages) - Add pnpm-lock.yaml for reproducible frontend dependency installs - Add .npmrc with allow-build=esbuild for Vite/Rollup native bindings Co-Authored-By: Claude Sonnet 4.6 --- qubes-gui/.npmrc | 1 + qubes-gui/pnpm-lock.yaml | 4041 ++++++++++++++++++++++++++++++++ qubes-gui/src-tauri/Cargo.lock | 2155 +++++++++-------- qubes-gui/src-tauri/Cargo.toml | 8 +- 4 files changed, 5123 insertions(+), 1082 deletions(-) create mode 100644 qubes-gui/.npmrc create mode 100644 qubes-gui/pnpm-lock.yaml diff --git a/qubes-gui/.npmrc b/qubes-gui/.npmrc new file mode 100644 index 0000000..c688721 --- /dev/null +++ b/qubes-gui/.npmrc @@ -0,0 +1 @@ +allow-build=esbuild diff --git a/qubes-gui/pnpm-lock.yaml b/qubes-gui/pnpm-lock.yaml new file mode 100644 index 0000000..ce1fe80 --- /dev/null +++ b/qubes-gui/pnpm-lock.yaml @@ -0,0 +1,4041 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@bitauth/libauth': + specifier: ^3.1.0-next.8 + version: 3.1.0-next.8 + '@dnd-kit/core': + specifier: ^6.3.1 + version: 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@dnd-kit/sortable': + specifier: ^10.0.0 + version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + '@dnd-kit/utilities': + specifier: ^3.2.2 + version: 3.2.2(react@19.2.4) + '@noble/curves': + specifier: ^1.9.7 + version: 1.9.7 + '@noble/hashes': + specifier: ^1.8.0 + version: 1.8.0 + '@tauri-apps/api': + specifier: ^2 + version: 2.10.1 + '@tauri-apps/plugin-dialog': + specifier: ^2.4.0 + version: 2.6.0 + '@tauri-apps/plugin-fs': + specifier: ^2.4.2 + version: 2.4.5 + '@tauri-apps/plugin-opener': + specifier: ^2 + version: 2.5.3 + '@tauri-apps/plugin-process': + specifier: ^2.3.0 + version: 2.3.1 + '@tauri-apps/plugin-shell': + specifier: ^2.3.1 + version: 2.3.5 + '@tauri-apps/plugin-updater': + specifier: ^2 + version: 2.10.0 + '@types/d3': + specifier: ^7.4.3 + version: 7.4.3 + '@walletconnect/modal': + specifier: ^2.7.0 + version: 2.7.0(@types/react@19.2.14)(react@19.2.4) + '@walletconnect/sign-client': + specifier: ^2.23.7 + version: 2.23.8(typescript@5.9.3) + '@xyflow/react': + specifier: ^12.9.0 + version: 12.10.1(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + chess.js: + specifier: ^1.4.0 + version: 1.4.0 + d3: + specifier: ^7.9.0 + version: 7.9.0 + emoji-picker-react: + specifier: ^4.15.0 + version: 4.18.0(react@19.2.4) + framer-motion: + specifier: ^12.29.2 + version: 12.37.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + qrcode.react: + specifier: ^4.2.0 + version: 4.2.0(react@19.2.4) + react: + specifier: ^19.1.0 + version: 19.2.4 + react-chessboard: + specifier: ^5.8.6 + version: 5.10.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react-dom: + specifier: ^19.1.0 + version: 19.2.4(react@19.2.4) + react-easy-crop: + specifier: ^5.5.6 + version: 5.5.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + recharts: + specifier: ^3.3.0 + version: 3.8.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react-is@19.2.4)(react@19.2.4)(redux@5.0.1) + zustand: + specifier: ^5.0.8 + version: 5.0.12(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) + devDependencies: + '@tauri-apps/cli': + specifier: ^2 + version: 2.10.1 + '@types/react': + specifier: ^19.1.8 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.1.6 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^4.6.0 + version: 4.7.0(vite@7.3.1(jiti@1.21.7)) + autoprefixer: + specifier: ^10.4.21 + version: 10.4.27(postcss@8.5.8) + postcss: + specifier: ^8.5.6 + version: 8.5.8 + tailwindcss: + specifier: ^3.4.18 + version: 3.4.19 + typescript: + specifier: ~5.9.3 + version: 5.9.3 + vite: + specifier: ^7.1.11 + version: 7.3.1(jiti@1.21.7) + +packages: + + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + 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==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@bitauth/libauth@3.1.0-next.8': + resolution: {integrity: sha512-Pm+Ju+YP3JeBLLTiVrBnia2wwE4G17r4XqpvPRMcklElJTe8J6x3JgKRg1by0Xm3ZY6UFxACkEAoSA+x419/zA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + '@dnd-kit/accessibility@3.1.1': + resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} + peerDependencies: + react: '>=16.8.0' + + '@dnd-kit/core@6.3.1': + resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@dnd-kit/modifiers@9.0.0': + resolution: {integrity: sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==} + peerDependencies: + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' + + '@dnd-kit/sortable@10.0.0': + resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==} + peerDependencies: + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' + + '@dnd-kit/utilities@3.2.2': + resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} + peerDependencies: + react: '>=16.8.0' + + '@esbuild/aix-ppc64@0.27.4': + resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.4': + resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.4': + resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.4': + resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.4': + resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.4': + resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.4': + resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.4': + resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.4': + resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.4': + resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.4': + resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.4': + resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.4': + resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.4': + resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.4': + resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.4': + resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.4': + resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.4': + resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.4': + resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.4': + resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.4': + resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.4': + resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.4': + resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.4': + resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.4': + resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.4': + resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@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/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@lit-labs/ssr-dom-shim@1.5.1': + resolution: {integrity: sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==} + + '@lit/reactive-element@1.6.3': + resolution: {integrity: sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ==} + + '@motionone/animation@10.18.0': + resolution: {integrity: sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==} + + '@motionone/dom@10.18.0': + resolution: {integrity: sha512-bKLP7E0eyO4B2UaHBBN55tnppwRnaE3KFfh3Ps9HhnAkar3Cb69kUCJY9as8LrccVYKgHA+JY5dOQqJLOPhF5A==} + + '@motionone/easing@10.18.0': + resolution: {integrity: sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==} + + '@motionone/generators@10.18.0': + resolution: {integrity: sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==} + + '@motionone/svelte@10.16.4': + resolution: {integrity: sha512-zRVqk20lD1xqe+yEDZhMYgftsuHc25+9JSo+r0a0OWUJFocjSV9D/+UGhX4xgJsuwB9acPzXLr20w40VnY2PQA==} + + '@motionone/types@10.17.1': + resolution: {integrity: sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==} + + '@motionone/utils@10.18.0': + resolution: {integrity: sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==} + + '@motionone/vue@10.16.4': + resolution: {integrity: sha512-z10PF9JV6SbjFq+/rYabM+8CVlMokgl8RFGvieSGNTmrkQanfHn+15XBrhG3BgUfvmTeSeyShfOHpG0i9zEdcg==} + deprecated: Motion One for Vue is deprecated. Use Oku Motion instead https://oku-ui.com/motion + + '@msgpack/msgpack@3.1.3': + resolution: {integrity: sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==} + engines: {node: '>= 18'} + + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.8.0': + resolution: {integrity: sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.7.0': + resolution: {integrity: sha512-HXydb0DgzTpDPwbVeDGCG1gIu7X6+AuU6Zl6av/E/KG8LMsvPntvq+w17CHRpKBmN6Ybdrt1eP3k4cj8DJa78w==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@reduxjs/toolkit@2.11.2': + resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.59.0': + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.59.0': + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.59.0': + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.59.0': + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.59.0': + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.59.0': + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.59.0': + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.59.0': + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.59.0': + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.59.0': + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.59.0': + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.59.0': + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.59.0': + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.59.0': + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.59.0': + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.59.0': + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.59.0': + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.59.0': + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.59.0': + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.59.0': + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + cpu: [x64] + os: [win32] + + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + + '@tauri-apps/api@2.10.1': + resolution: {integrity: sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==} + + '@tauri-apps/cli-darwin-arm64@2.10.1': + resolution: {integrity: sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tauri-apps/cli-darwin-x64@2.10.1': + resolution: {integrity: sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tauri-apps/cli-linux-arm-gnueabihf@2.10.1': + resolution: {integrity: sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tauri-apps/cli-linux-arm64-gnu@2.10.1': + resolution: {integrity: sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tauri-apps/cli-linux-arm64-musl@2.10.1': + resolution: {integrity: sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tauri-apps/cli-linux-riscv64-gnu@2.10.1': + resolution: {integrity: sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@tauri-apps/cli-linux-x64-gnu@2.10.1': + resolution: {integrity: sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tauri-apps/cli-linux-x64-musl@2.10.1': + resolution: {integrity: sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tauri-apps/cli-win32-arm64-msvc@2.10.1': + resolution: {integrity: sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tauri-apps/cli-win32-ia32-msvc@2.10.1': + resolution: {integrity: sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@tauri-apps/cli-win32-x64-msvc@2.10.1': + resolution: {integrity: sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tauri-apps/cli@2.10.1': + resolution: {integrity: sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g==} + engines: {node: '>= 10'} + hasBin: true + + '@tauri-apps/plugin-dialog@2.6.0': + resolution: {integrity: sha512-q4Uq3eY87TdcYzXACiYSPhmpBA76shgmQswGkSVio4C82Sz2W4iehe9TnKYwbq7weHiL88Yw19XZm7v28+Micg==} + + '@tauri-apps/plugin-fs@2.4.5': + resolution: {integrity: sha512-dVxWWGE6VrOxC7/jlhyE+ON/Cc2REJlM35R3PJX3UvFw2XwYhLGQVAIyrehenDdKjotipjYEVc4YjOl3qq90fA==} + + '@tauri-apps/plugin-opener@2.5.3': + resolution: {integrity: sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ==} + + '@tauri-apps/plugin-process@2.3.1': + resolution: {integrity: sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==} + + '@tauri-apps/plugin-shell@2.3.5': + resolution: {integrity: sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg==} + + '@tauri-apps/plugin-updater@2.10.0': + resolution: {integrity: sha512-ljN8jPlnT0aSn8ecYhuBib84alxfMx6Hc8vJSKMJyzGbTPFZAC44T2I1QNFZssgWKrAlofvJqCC6Rr472JWfkQ==} + + '@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__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/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.3': + resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@walletconnect/core@2.23.8': + resolution: {integrity: sha512-559+fA6Hh9CkEIOtrWKdDWoa3HL47glDF7D75LbqQzv4v325KXq24KEsjzDPBYr7pI49gQo7P2HpPnY1ax+8Aw==} + engines: {node: '>=18.20.8'} + + '@walletconnect/environment@1.0.1': + resolution: {integrity: sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==} + + '@walletconnect/events@1.0.1': + resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==} + + '@walletconnect/heartbeat@1.2.2': + resolution: {integrity: sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==} + + '@walletconnect/jsonrpc-provider@1.0.14': + resolution: {integrity: sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==} + + '@walletconnect/jsonrpc-types@1.0.4': + resolution: {integrity: sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==} + + '@walletconnect/jsonrpc-utils@1.0.8': + resolution: {integrity: sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==} + + '@walletconnect/jsonrpc-ws-connection@1.0.16': + resolution: {integrity: sha512-G81JmsMqh5nJheE1mPst1W0WfVv0SG3N7JggwLLGnI7iuDZJq8cRJvQwLGKHn5H1WTW7DEPCo00zz5w62AbL3Q==} + + '@walletconnect/keyvaluestorage@1.1.1': + resolution: {integrity: sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==} + peerDependencies: + '@react-native-async-storage/async-storage': 1.x + peerDependenciesMeta: + '@react-native-async-storage/async-storage': + optional: true + + '@walletconnect/logger@3.0.2': + resolution: {integrity: sha512-7wR3wAwJTOmX4gbcUZcFMov8fjftY05+5cO/d4cpDD8wDzJ+cIlKdYOXaXfxHLSYeDazMXIsxMYjHYVDfkx+nA==} + + '@walletconnect/modal-core@2.7.0': + resolution: {integrity: sha512-oyMIfdlNdpyKF2kTJowTixZSo0PGlCJRdssUN/EZdA6H6v03hZnf09JnwpljZNfir2M65Dvjm/15nGrDQnlxSA==} + + '@walletconnect/modal-ui@2.7.0': + resolution: {integrity: sha512-gERYvU7D7K1ANCN/8vUgsE0d2hnRemfAFZ2novm9aZBg7TEd/4EgB+AqbJ+1dc7GhOL6dazckVq78TgccHb7mQ==} + + '@walletconnect/modal@2.7.0': + resolution: {integrity: sha512-RQVt58oJ+rwqnPcIvRFeMGKuXb9qkgSmwz4noF8JZGUym3gUAzVs+uW2NQ1Owm9XOJAV+sANrtJ+VoVq1ftElw==} + deprecated: Please follow the migration guide on https://docs.reown.com/appkit/upgrade/wcm + + '@walletconnect/relay-api@1.0.11': + resolution: {integrity: sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==} + + '@walletconnect/relay-auth@1.1.0': + resolution: {integrity: sha512-qFw+a9uRz26jRCDgL7Q5TA9qYIgcNY8jpJzI1zAWNZ8i7mQjaijRnWFKsCHAU9CyGjvt6RKrRXyFtFOpWTVmCQ==} + + '@walletconnect/safe-json@1.0.2': + resolution: {integrity: sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==} + + '@walletconnect/sign-client@2.23.8': + resolution: {integrity: sha512-7DtFDQZwOK4E9q+TKWL819d01dpNHA3jMcntSsQqSLNU34orbkDB/BJzW4nyWZ6H9DuGHRvibJA9wvfXjOCWBw==} + + '@walletconnect/time@1.0.2': + resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==} + + '@walletconnect/types@2.23.8': + resolution: {integrity: sha512-OI/0Z7/8r11EDU9bBPy5nixYgsk6SrTcOvWe9r7Nf2WvkMcPLgV7aS8rb6+nInRmDPfXuyTgzdAox0rtmfJMzg==} + + '@walletconnect/utils@2.23.8': + resolution: {integrity: sha512-vJrRrZFZANWmnEEnWnfVSnpQ+jdjqBb5fqSgp0VGeRX3pNr2KAHJ0TwNnEN+fbhR76JxuFrpcY7HJUT7DHDJ7w==} + + '@walletconnect/window-getters@1.0.1': + resolution: {integrity: sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==} + + '@walletconnect/window-metadata@1.0.1': + resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==} + + '@xyflow/react@12.10.1': + resolution: {integrity: sha512-5eSWtIK/+rkldOuFbOOz44CRgQRjtS9v5nufk77DV+XBnfCGL9HAQ8PG00o2ZYKqkEU/Ak6wrKC95Tu+2zuK3Q==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + '@xyflow/system@0.0.75': + resolution: {integrity: sha512-iXs+AGFLi8w/VlAoc/iSxk+CxfT6o64Uw/k0CKASOPqjqz6E0rb5jFZgJtXGZCpfQI6OQpu5EnumP5fGxQheaQ==} + + abitype@1.2.3: + resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + autoprefixer@10.4.27: + resolution: {integrity: sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + baseline-browser-mapping@2.10.8: + resolution: {integrity: sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + blakejs@1.2.1: + resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001779: + resolution: {integrity: sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==} + + chess.js@1.4.0: + resolution: {integrity: sha512-BBJgrrtKQOzFLonR0l+k64A98NLemPwNsCskwb+29bRwobUa4iTm51E1kwGPbWXAcfdDa18nad6vpPPKPWarqw==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@1.2.2: + resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + delaunator@5.0.1: + resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-browser@5.3.0: + resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + electron-to-chromium@1.5.313: + resolution: {integrity: sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==} + + emoji-picker-react@4.18.0: + resolution: {integrity: sha512-vLTrLfApXAIciguGE57pXPWs9lPLBspbEpPMiUq03TIli2dHZBiB+aZ0R9/Wat0xmTfcd4AuEzQgSYxEZ8C88Q==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16' + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encode-utf8@1.0.3: + resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} + + es-toolkit@1.44.0: + resolution: {integrity: sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==} + + es-toolkit@1.45.1: + resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} + + esbuild@0.27.4: + resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + flairup@1.0.0: + resolution: {integrity: sha512-IKlE+pNvL2R+kVL1kEhUYqRxVqeFnjiIvHWDMLFXNaqyUdFXQM2wte44EfMYJNHkW16X991t2Zg8apKkhv7OBA==} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + framer-motion@12.37.0: + resolution: {integrity: sha512-j/QUcZS9Nw3NzZWoAbkzr3ETRFHyVeQMlGOUYUmG15U+uiyn9DqIktYruVPDcqY8I35qYR70JaZBvFmS6p+Pdg==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + h3@1.15.6: + resolution: {integrity: sha512-oi15ESLW5LRthZ+qPCi5GNasY/gvynSKUQxgiovrY63bPAtG59wtM+LSrlcwvOHAXzGrXVLnI97brbkdPF9WoQ==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hey-listen@1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + idb-keyval@6.2.2: + resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} + + immer@10.2.0: + resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} + + immer@11.1.4: + resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keyvaluestorage-interface@1.0.0: + resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lit-element@3.3.3: + resolution: {integrity: sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA==} + + lit-html@2.8.0: + resolution: {integrity: sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q==} + + lit@2.8.0: + resolution: {integrity: sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + lru-cache@11.2.7: + resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + motion-dom@12.37.0: + resolution: {integrity: sha512-LnppZuwX1jQizRWTl9LBLMN3RbAEmdQkX/2Af0UW70NCqYJI/7GfI83vQP9Ucel/Avc0Tf2ZWy8FHawuc0O6Vg==} + + motion-utils@12.36.0: + resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} + + motion@10.16.2: + resolution: {integrity: sha512-p+PurYqfUdcJZvtnmAqu5fJgV2kR0uLFQuBKtLeFVTrYEVllI99tiOTSefVNYuip9ELTEkepIIDftNdze76NAQ==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multiformats@9.9.0: + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-mock-http@1.0.4: + resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + + node-releases@2.0.36: + resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-wheel@1.0.1: + resolution: {integrity: sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + ox@0.9.3: + resolution: {integrity: sha512-KzyJP+fPV4uhuuqrTZyok4DC7vFzi7HLUFiUNEmpbyh59htKWkOC98IONC1zgXJPbHAhQgqs6B0Z6StCGhmQvg==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@10.0.0: + resolution: {integrity: sha512-eI9pKwWEix40kfvSzqEP6ldqOoBIN7dwD/o91TY5z8vQI12sAffpR/pOqAD1IVVwIVHDpHjkq0joBPdJD0rafA==} + hasBin: true + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + engines: {node: ^10 || ^12 || >=14} + + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + + proxy-compare@2.5.1: + resolution: {integrity: sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==} + + qrcode.react@4.2.0: + resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + qrcode@1.5.3: + resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} + engines: {node: '>=10.13.0'} + hasBin: true + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + react-chessboard@5.10.0: + resolution: {integrity: sha512-Y3PgaCVhnDG3IaQfu86OzTSEIEAUtuU5XwmHWnx3tcFOX7lSoAq81ZFX3MBj6y5a6FzDMTczMVmkkrV2CzTrIw==} + engines: {node: '>=20.11.0', pnpm: '>=9.4.0'} + peerDependencies: + react: ^19.0.0 + react-dom: ^19.0.0 + + react-dom@19.2.4: + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} + peerDependencies: + react: ^19.2.4 + + react-easy-crop@5.5.6: + resolution: {integrity: sha512-Jw3/ozs8uXj3NpL511Suc4AHY+mLRO23rUgipXvNYKqezcFSYHxe4QXibBymkOoY6oOtLVMPO2HNPRHYvMPyTw==} + peerDependencies: + react: '>=16.4.0' + react-dom: '>=16.4.0' + + react-is@19.2.4: + resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==} + + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react@19.2.4: + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + recharts@3.8.0: + resolution: {integrity: sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ==} + engines: {node: '>=18'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + robust-predicates@3.0.2: + resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + + rollup@4.59.0: + resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + slow-redact@0.3.2: + resolution: {integrity: sha512-MseHyi2+E/hBRqdOi5COy6wZ7j7DxXRz9NkseavNYSvvWC06D8a5cidVZX3tcG5eCW3NIyVU4zT63hw0Q486jw==} + + sonic-boom@4.2.1: + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwindcss@3.4.19: + resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} + engines: {node: '>=14.0.0'} + hasBin: true + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.3: + resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + + uint8arrays@3.1.1: + resolution: {integrity: sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + unstorage@1.17.4: + resolution: {integrity: sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + 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 + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + valtio@1.11.2: + resolution: {integrity: sha512-1XfIxnUXzyswPAPXo1P3Pdx2mq/pIqZICkWN60Hby0d9Iqb+MEIpqgYVlbflvHdrp2YR/q3jyKWRPJJ100yxaw==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=16.8' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + + victory-vendor@37.3.6: + resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} + + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + 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 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + + zustand@4.5.7: + resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + + zustand@5.0.12: + resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==} + 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 + +snapshots: + + '@adraffy/ens-normalize@1.11.1': {} + + '@alloc/quick-lru@5.2.0': {} + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bitauth/libauth@3.1.0-next.8': {} + + '@dnd-kit/accessibility@3.1.1(react@19.2.4)': + dependencies: + react: 19.2.4 + tslib: 2.8.1 + + '@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@dnd-kit/accessibility': 3.1.1(react@19.2.4) + '@dnd-kit/utilities': 3.2.2(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + tslib: 2.8.1 + + '@dnd-kit/modifiers@9.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@dnd-kit/utilities': 3.2.2(react@19.2.4) + react: 19.2.4 + tslib: 2.8.1 + + '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)': + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@dnd-kit/utilities': 3.2.2(react@19.2.4) + react: 19.2.4 + tslib: 2.8.1 + + '@dnd-kit/utilities@3.2.2(react@19.2.4)': + dependencies: + react: 19.2.4 + tslib: 2.8.1 + + '@esbuild/aix-ppc64@0.27.4': + optional: true + + '@esbuild/android-arm64@0.27.4': + optional: true + + '@esbuild/android-arm@0.27.4': + optional: true + + '@esbuild/android-x64@0.27.4': + optional: true + + '@esbuild/darwin-arm64@0.27.4': + optional: true + + '@esbuild/darwin-x64@0.27.4': + optional: true + + '@esbuild/freebsd-arm64@0.27.4': + optional: true + + '@esbuild/freebsd-x64@0.27.4': + optional: true + + '@esbuild/linux-arm64@0.27.4': + optional: true + + '@esbuild/linux-arm@0.27.4': + optional: true + + '@esbuild/linux-ia32@0.27.4': + optional: true + + '@esbuild/linux-loong64@0.27.4': + optional: true + + '@esbuild/linux-mips64el@0.27.4': + optional: true + + '@esbuild/linux-ppc64@0.27.4': + optional: true + + '@esbuild/linux-riscv64@0.27.4': + optional: true + + '@esbuild/linux-s390x@0.27.4': + optional: true + + '@esbuild/linux-x64@0.27.4': + optional: true + + '@esbuild/netbsd-arm64@0.27.4': + optional: true + + '@esbuild/netbsd-x64@0.27.4': + optional: true + + '@esbuild/openbsd-arm64@0.27.4': + optional: true + + '@esbuild/openbsd-x64@0.27.4': + optional: true + + '@esbuild/openharmony-arm64@0.27.4': + optional: true + + '@esbuild/sunos-x64@0.27.4': + optional: true + + '@esbuild/win32-arm64@0.27.4': + optional: true + + '@esbuild/win32-ia32@0.27.4': + optional: true + + '@esbuild/win32-x64@0.27.4': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@lit-labs/ssr-dom-shim@1.5.1': {} + + '@lit/reactive-element@1.6.3': + dependencies: + '@lit-labs/ssr-dom-shim': 1.5.1 + + '@motionone/animation@10.18.0': + dependencies: + '@motionone/easing': 10.18.0 + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/dom@10.18.0': + dependencies: + '@motionone/animation': 10.18.0 + '@motionone/generators': 10.18.0 + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + hey-listen: 1.0.8 + tslib: 2.8.1 + + '@motionone/easing@10.18.0': + dependencies: + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/generators@10.18.0': + dependencies: + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + tslib: 2.8.1 + + '@motionone/svelte@10.16.4': + dependencies: + '@motionone/dom': 10.18.0 + tslib: 2.8.1 + + '@motionone/types@10.17.1': {} + + '@motionone/utils@10.18.0': + dependencies: + '@motionone/types': 10.17.1 + hey-listen: 1.0.8 + tslib: 2.8.1 + + '@motionone/vue@10.16.4': + dependencies: + '@motionone/dom': 10.18.0 + tslib: 2.8.1 + + '@msgpack/msgpack@3.1.3': {} + + '@noble/ciphers@1.3.0': {} + + '@noble/curves@1.8.0': + dependencies: + '@noble/hashes': 1.7.0 + + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.7.0': {} + + '@noble/hashes@1.8.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1))(react@19.2.4)': + dependencies: + '@standard-schema/spec': 1.1.0 + '@standard-schema/utils': 0.3.0 + immer: 11.1.4 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 19.2.4 + react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1) + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.59.0': + optional: true + + '@rollup/rollup-android-arm64@4.59.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.59.0': + optional: true + + '@rollup/rollup-darwin-x64@4.59.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.59.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.59.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.59.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.59.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.59.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.59.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.59.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.59.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.59.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.59.0': + optional: true + + '@scure/base@1.2.6': {} + + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@standard-schema/spec@1.1.0': {} + + '@standard-schema/utils@0.3.0': {} + + '@tauri-apps/api@2.10.1': {} + + '@tauri-apps/cli-darwin-arm64@2.10.1': + optional: true + + '@tauri-apps/cli-darwin-x64@2.10.1': + optional: true + + '@tauri-apps/cli-linux-arm-gnueabihf@2.10.1': + optional: true + + '@tauri-apps/cli-linux-arm64-gnu@2.10.1': + optional: true + + '@tauri-apps/cli-linux-arm64-musl@2.10.1': + optional: true + + '@tauri-apps/cli-linux-riscv64-gnu@2.10.1': + optional: true + + '@tauri-apps/cli-linux-x64-gnu@2.10.1': + optional: true + + '@tauri-apps/cli-linux-x64-musl@2.10.1': + optional: true + + '@tauri-apps/cli-win32-arm64-msvc@2.10.1': + optional: true + + '@tauri-apps/cli-win32-ia32-msvc@2.10.1': + optional: true + + '@tauri-apps/cli-win32-x64-msvc@2.10.1': + optional: true + + '@tauri-apps/cli@2.10.1': + optionalDependencies: + '@tauri-apps/cli-darwin-arm64': 2.10.1 + '@tauri-apps/cli-darwin-x64': 2.10.1 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.10.1 + '@tauri-apps/cli-linux-arm64-gnu': 2.10.1 + '@tauri-apps/cli-linux-arm64-musl': 2.10.1 + '@tauri-apps/cli-linux-riscv64-gnu': 2.10.1 + '@tauri-apps/cli-linux-x64-gnu': 2.10.1 + '@tauri-apps/cli-linux-x64-musl': 2.10.1 + '@tauri-apps/cli-win32-arm64-msvc': 2.10.1 + '@tauri-apps/cli-win32-ia32-msvc': 2.10.1 + '@tauri-apps/cli-win32-x64-msvc': 2.10.1 + + '@tauri-apps/plugin-dialog@2.6.0': + dependencies: + '@tauri-apps/api': 2.10.1 + + '@tauri-apps/plugin-fs@2.4.5': + dependencies: + '@tauri-apps/api': 2.10.1 + + '@tauri-apps/plugin-opener@2.5.3': + dependencies: + '@tauri-apps/api': 2.10.1 + + '@tauri-apps/plugin-process@2.3.1': + dependencies: + '@tauri-apps/api': 2.10.1 + + '@tauri-apps/plugin-shell@2.3.5': + dependencies: + '@tauri-apps/api': 2.10.1 + + '@tauri-apps/plugin-updater@2.10.0': + dependencies: + '@tauri-apps/api': 2.10.1 + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + + '@types/estree@1.0.8': {} + + '@types/geojson@7946.0.16': {} + + '@types/react-dom@19.2.3(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + + '@types/trusted-types@2.0.7': {} + + '@types/use-sync-external-store@0.0.6': {} + + '@vitejs/plugin-react@4.7.0(vite@7.3.1(jiti@1.21.7))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 7.3.1(jiti@1.21.7) + transitivePeerDependencies: + - supports-color + + '@walletconnect/core@2.23.8(typescript@5.9.3)': + dependencies: + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-provider': 1.0.14 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/jsonrpc-ws-connection': 1.0.16 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.8 + '@walletconnect/utils': 2.23.8(typescript@5.9.3) + '@walletconnect/window-getters': 1.0.1 + es-toolkit: 1.44.0 + events: 3.3.0 + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/environment@1.0.1': + dependencies: + tslib: 1.14.1 + + '@walletconnect/events@1.0.1': + dependencies: + keyvaluestorage-interface: 1.0.0 + tslib: 1.14.1 + + '@walletconnect/heartbeat@1.2.2': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/time': 1.0.2 + events: 3.3.0 + + '@walletconnect/jsonrpc-provider@1.0.14': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + + '@walletconnect/jsonrpc-types@1.0.4': + dependencies: + events: 3.3.0 + keyvaluestorage-interface: 1.0.0 + + '@walletconnect/jsonrpc-utils@1.0.8': + dependencies: + '@walletconnect/environment': 1.0.1 + '@walletconnect/jsonrpc-types': 1.0.4 + tslib: 1.14.1 + + '@walletconnect/jsonrpc-ws-connection@1.0.16': + dependencies: + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/safe-json': 1.0.2 + events: 3.3.0 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@walletconnect/keyvaluestorage@1.1.1': + dependencies: + '@walletconnect/safe-json': 1.0.2 + idb-keyval: 6.2.2 + unstorage: 1.17.4(idb-keyval@6.2.2) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/logger@3.0.2': + dependencies: + '@walletconnect/safe-json': 1.0.2 + pino: 10.0.0 + + '@walletconnect/modal-core@2.7.0(@types/react@19.2.14)(react@19.2.4)': + dependencies: + valtio: 1.11.2(@types/react@19.2.14)(react@19.2.4) + transitivePeerDependencies: + - '@types/react' + - react + + '@walletconnect/modal-ui@2.7.0(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@walletconnect/modal-core': 2.7.0(@types/react@19.2.14)(react@19.2.4) + lit: 2.8.0 + motion: 10.16.2 + qrcode: 1.5.3 + transitivePeerDependencies: + - '@types/react' + - react + + '@walletconnect/modal@2.7.0(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@walletconnect/modal-core': 2.7.0(@types/react@19.2.14)(react@19.2.4) + '@walletconnect/modal-ui': 2.7.0(@types/react@19.2.14)(react@19.2.4) + transitivePeerDependencies: + - '@types/react' + - react + + '@walletconnect/relay-api@1.0.11': + dependencies: + '@walletconnect/jsonrpc-types': 1.0.4 + + '@walletconnect/relay-auth@1.1.0': + dependencies: + '@noble/curves': 1.8.0 + '@noble/hashes': 1.7.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + uint8arrays: 3.1.1 + + '@walletconnect/safe-json@1.0.2': + dependencies: + tslib: 1.14.1 + + '@walletconnect/sign-client@2.23.8(typescript@5.9.3)': + dependencies: + '@walletconnect/core': 2.23.8(typescript@5.9.3) + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/logger': 3.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.8 + '@walletconnect/utils': 2.23.8(typescript@5.9.3) + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - ioredis + - typescript + - uploadthing + - utf-8-validate + - zod + + '@walletconnect/time@1.0.2': + dependencies: + tslib: 1.14.1 + + '@walletconnect/types@2.23.8': + dependencies: + '@walletconnect/events': 1.0.1 + '@walletconnect/heartbeat': 1.2.2 + '@walletconnect/jsonrpc-types': 1.0.4 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + events: 3.3.0 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - uploadthing + + '@walletconnect/utils@2.23.8(typescript@5.9.3)': + dependencies: + '@msgpack/msgpack': 3.1.3 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@walletconnect/jsonrpc-utils': 1.0.8 + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/logger': 3.0.2 + '@walletconnect/relay-api': 1.0.11 + '@walletconnect/relay-auth': 1.1.0 + '@walletconnect/safe-json': 1.0.2 + '@walletconnect/time': 1.0.2 + '@walletconnect/types': 2.23.8 + '@walletconnect/window-getters': 1.0.1 + '@walletconnect/window-metadata': 1.0.1 + blakejs: 1.2.1 + detect-browser: 5.3.0 + ox: 0.9.3(typescript@5.9.3) + uint8arrays: 3.1.1 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - ioredis + - typescript + - uploadthing + - zod + + '@walletconnect/window-getters@1.0.1': + dependencies: + tslib: 1.14.1 + + '@walletconnect/window-metadata@1.0.1': + dependencies: + '@walletconnect/window-getters': 1.0.1 + tslib: 1.14.1 + + '@xyflow/react@12.10.1(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@xyflow/system': 0.0.75 + classcat: 5.0.5 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + zustand: 4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4) + transitivePeerDependencies: + - '@types/react' + - immer + + '@xyflow/system@0.0.75': + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + + abitype@1.2.3(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + atomic-sleep@1.0.0: {} + + autoprefixer@10.4.27(postcss@8.5.8): + dependencies: + browserslist: 4.28.1 + caniuse-lite: 1.0.30001779 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.8 + postcss-value-parser: 4.2.0 + + baseline-browser-mapping@2.10.8: {} + + binary-extensions@2.3.0: {} + + blakejs@1.2.1: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.10.8 + caniuse-lite: 1.0.30001779 + electron-to-chromium: 1.5.313 + node-releases: 2.0.36 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + camelcase-css@2.0.1: {} + + camelcase@5.3.1: {} + + caniuse-lite@1.0.30001779: {} + + chess.js@1.4.0: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + classcat@5.0.5: {} + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@4.1.1: {} + + commander@7.2.0: {} + + convert-source-map@2.0.0: {} + + cookie-es@1.2.2: {} + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + cssesc@3.0.0: {} + + csstype@3.2.3: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.0.1 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.2 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decamelize@1.2.0: {} + + decimal.js-light@2.5.1: {} + + defu@6.1.4: {} + + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + + destr@2.0.5: {} + + detect-browser@5.3.0: {} + + didyoumean@1.2.2: {} + + dijkstrajs@1.0.3: {} + + dlv@1.1.3: {} + + electron-to-chromium@1.5.313: {} + + emoji-picker-react@4.18.0(react@19.2.4): + dependencies: + flairup: 1.0.0 + react: 19.2.4 + + emoji-regex@8.0.0: {} + + encode-utf8@1.0.3: {} + + es-toolkit@1.44.0: {} + + es-toolkit@1.45.1: {} + + esbuild@0.27.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.4 + '@esbuild/android-arm': 0.27.4 + '@esbuild/android-arm64': 0.27.4 + '@esbuild/android-x64': 0.27.4 + '@esbuild/darwin-arm64': 0.27.4 + '@esbuild/darwin-x64': 0.27.4 + '@esbuild/freebsd-arm64': 0.27.4 + '@esbuild/freebsd-x64': 0.27.4 + '@esbuild/linux-arm': 0.27.4 + '@esbuild/linux-arm64': 0.27.4 + '@esbuild/linux-ia32': 0.27.4 + '@esbuild/linux-loong64': 0.27.4 + '@esbuild/linux-mips64el': 0.27.4 + '@esbuild/linux-ppc64': 0.27.4 + '@esbuild/linux-riscv64': 0.27.4 + '@esbuild/linux-s390x': 0.27.4 + '@esbuild/linux-x64': 0.27.4 + '@esbuild/netbsd-arm64': 0.27.4 + '@esbuild/netbsd-x64': 0.27.4 + '@esbuild/openbsd-arm64': 0.27.4 + '@esbuild/openbsd-x64': 0.27.4 + '@esbuild/openharmony-arm64': 0.27.4 + '@esbuild/sunos-x64': 0.27.4 + '@esbuild/win32-arm64': 0.27.4 + '@esbuild/win32-ia32': 0.27.4 + '@esbuild/win32-x64': 0.27.4 + + escalade@3.2.0: {} + + eventemitter3@5.0.1: {} + + eventemitter3@5.0.4: {} + + events@3.3.0: {} + + fast-glob@3.3.3: + 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 + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + flairup@1.0.0: {} + + fraction.js@5.3.4: {} + + framer-motion@12.37.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + motion-dom: 12.37.0 + motion-utils: 12.36.0 + tslib: 2.8.1 + optionalDependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + h3@1.15.6: + dependencies: + cookie-es: 1.2.2 + crossws: 0.3.5 + defu: 6.1.4 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.4 + radix3: 1.1.2 + ufo: 1.6.3 + uncrypto: 0.1.3 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hey-listen@1.0.8: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + idb-keyval@6.2.2: {} + + immer@10.2.0: {} + + immer@11.1.4: {} + + internmap@2.0.3: {} + + iron-webcrypto@1.2.1: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + jiti@1.21.7: {} + + js-tokens@4.0.0: {} + + jsesc@3.1.0: {} + + json5@2.2.3: {} + + keyvaluestorage-interface@1.0.0: {} + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lit-element@3.3.3: + dependencies: + '@lit-labs/ssr-dom-shim': 1.5.1 + '@lit/reactive-element': 1.6.3 + lit-html: 2.8.0 + + lit-html@2.8.0: + dependencies: + '@types/trusted-types': 2.0.7 + + lit@2.8.0: + dependencies: + '@lit/reactive-element': 1.6.3 + lit-element: 3.3.3 + lit-html: 2.8.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + lru-cache@11.2.7: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + motion-dom@12.37.0: + dependencies: + motion-utils: 12.36.0 + + motion-utils@12.36.0: {} + + motion@10.16.2: + dependencies: + '@motionone/animation': 10.18.0 + '@motionone/dom': 10.18.0 + '@motionone/svelte': 10.16.4 + '@motionone/types': 10.17.1 + '@motionone/utils': 10.18.0 + '@motionone/vue': 10.16.4 + + ms@2.1.3: {} + + multiformats@9.9.0: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.11: {} + + node-fetch-native@1.6.7: {} + + node-mock-http@1.0.4: {} + + node-releases@2.0.36: {} + + normalize-path@3.0.0: {} + + normalize-wheel@1.0.1: {} + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.3 + + on-exit-leak-free@2.1.2: {} + + ox@0.9.3(typescript@5.9.3): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-try@2.2.0: {} + + path-exists@4.0.0: {} + + path-parse@1.0.7: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pify@2.3.0: {} + + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-std-serializers@7.1.0: {} + + pino@10.0.0: + dependencies: + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + slow-redact: 0.3.2 + sonic-boom: 4.2.1 + thread-stream: 3.1.0 + + pirates@4.0.7: {} + + pngjs@5.0.0: {} + + postcss-import@15.1.0(postcss@8.5.8): + dependencies: + postcss: 8.5.8 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.11 + + postcss-js@4.1.0(postcss@8.5.8): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.8 + + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.8): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 1.21.7 + postcss: 8.5.8 + + postcss-nested@6.2.0(postcss@8.5.8): + dependencies: + postcss: 8.5.8 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.8: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + process-warning@5.0.0: {} + + proxy-compare@2.5.1: {} + + qrcode.react@4.2.0(react@19.2.4): + dependencies: + react: 19.2.4 + + qrcode@1.5.3: + dependencies: + dijkstrajs: 1.0.3 + encode-utf8: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + queue-microtask@1.2.3: {} + + quick-format-unescaped@4.0.4: {} + + radix3@1.1.2: {} + + react-chessboard@5.10.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@dnd-kit/modifiers': 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + + react-dom@19.2.4(react@19.2.4): + dependencies: + react: 19.2.4 + scheduler: 0.27.0 + + react-easy-crop@5.5.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + normalize-wheel: 1.0.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + tslib: 2.8.1 + + react-is@19.2.4: {} + + react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 19.2.4 + use-sync-external-store: 1.6.0(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + redux: 5.0.1 + + react-refresh@0.17.0: {} + + react@19.2.4: {} + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@5.0.0: {} + + real-require@0.2.0: {} + + recharts@3.8.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react-is@19.2.4)(react@19.2.4)(redux@5.0.1): + dependencies: + '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1))(react@19.2.4) + clsx: 2.1.1 + decimal.js-light: 2.5.1 + es-toolkit: 1.45.1 + eventemitter3: 5.0.4 + immer: 10.2.0 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + react-is: 19.2.4 + react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1) + reselect: 5.1.1 + tiny-invariant: 1.3.3 + use-sync-external-store: 1.6.0(react@19.2.4) + victory-vendor: 37.3.6 + transitivePeerDependencies: + - '@types/react' + - redux + + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + + require-directory@2.1.1: {} + + require-main-filename@2.0.0: {} + + reselect@5.1.1: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + robust-predicates@3.0.2: {} + + rollup@4.59.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.59.0 + '@rollup/rollup-android-arm64': 4.59.0 + '@rollup/rollup-darwin-arm64': 4.59.0 + '@rollup/rollup-darwin-x64': 4.59.0 + '@rollup/rollup-freebsd-arm64': 4.59.0 + '@rollup/rollup-freebsd-x64': 4.59.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 + '@rollup/rollup-linux-arm-musleabihf': 4.59.0 + '@rollup/rollup-linux-arm64-gnu': 4.59.0 + '@rollup/rollup-linux-arm64-musl': 4.59.0 + '@rollup/rollup-linux-loong64-gnu': 4.59.0 + '@rollup/rollup-linux-loong64-musl': 4.59.0 + '@rollup/rollup-linux-ppc64-gnu': 4.59.0 + '@rollup/rollup-linux-ppc64-musl': 4.59.0 + '@rollup/rollup-linux-riscv64-gnu': 4.59.0 + '@rollup/rollup-linux-riscv64-musl': 4.59.0 + '@rollup/rollup-linux-s390x-gnu': 4.59.0 + '@rollup/rollup-linux-x64-gnu': 4.59.0 + '@rollup/rollup-linux-x64-musl': 4.59.0 + '@rollup/rollup-openbsd-x64': 4.59.0 + '@rollup/rollup-openharmony-arm64': 4.59.0 + '@rollup/rollup-win32-arm64-msvc': 4.59.0 + '@rollup/rollup-win32-ia32-msvc': 4.59.0 + '@rollup/rollup-win32-x64-gnu': 4.59.0 + '@rollup/rollup-win32-x64-msvc': 4.59.0 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rw@1.3.3: {} + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + set-blocking@2.0.0: {} + + slow-redact@0.3.2: {} + + sonic-boom@4.2.1: + dependencies: + atomic-sleep: 1.0.0 + + source-map-js@1.2.1: {} + + split2@4.2.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.15 + ts-interface-checker: 0.1.13 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwindcss@3.4.19: + 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 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.8 + postcss-import: 15.1.0(postcss@8.5.8) + postcss-js: 4.1.0(postcss@8.5.8) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.8) + postcss-nested: 6.2.0(postcss@8.5.8) + postcss-selector-parser: 6.1.2 + resolve: 1.22.11 + sucrase: 3.35.1 + transitivePeerDependencies: + - tsx + - yaml + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + + tiny-invariant@1.3.3: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-interface-checker@0.1.13: {} + + tslib@1.14.1: {} + + tslib@2.8.1: {} + + typescript@5.9.3: {} + + ufo@1.6.3: {} + + uint8arrays@3.1.1: + dependencies: + multiformats: 9.9.0 + + uncrypto@0.1.3: {} + + unstorage@1.17.4(idb-keyval@6.2.2): + dependencies: + anymatch: 3.1.3 + chokidar: 5.0.0 + destr: 2.0.5 + h3: 1.15.6 + lru-cache: 11.2.7 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.3 + optionalDependencies: + idb-keyval: 6.2.2 + + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + use-sync-external-store@1.2.0(react@19.2.4): + dependencies: + react: 19.2.4 + + use-sync-external-store@1.6.0(react@19.2.4): + dependencies: + react: 19.2.4 + + util-deprecate@1.0.2: {} + + valtio@1.11.2(@types/react@19.2.14)(react@19.2.4): + dependencies: + proxy-compare: 2.5.1 + use-sync-external-store: 1.2.0(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + react: 19.2.4 + + victory-vendor@37.3.6: + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + + vite@7.3.1(jiti@1.21.7): + dependencies: + esbuild: 0.27.4 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.8 + rollup: 4.59.0 + tinyglobby: 0.2.15 + optionalDependencies: + fsevents: 2.3.3 + jiti: 1.21.7 + + which-module@2.0.1: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + ws@7.5.10: {} + + y18n@4.0.3: {} + + yallist@3.1.1: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + zustand@4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4): + dependencies: + use-sync-external-store: 1.6.0(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.14 + immer: 11.1.4 + react: 19.2.4 + + zustand@5.0.12(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): + optionalDependencies: + '@types/react': 19.2.14 + immer: 11.1.4 + react: 19.2.4 + use-sync-external-store: 1.6.0(react@19.2.4) diff --git a/qubes-gui/src-tauri/Cargo.lock b/qubes-gui/src-tauri/Cargo.lock index 0e275cb..5702f7a 100644 --- a/qubes-gui/src-tauri/Cargo.lock +++ b/qubes-gui/src-tauri/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - [[package]] name = "adler2" version = "2.0.1" @@ -19,9 +10,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -52,9 +43,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arbitrary" @@ -65,27 +56,6 @@ dependencies = [ "derive_arbitrary", ] -[[package]] -name = "ashpd" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" -dependencies = [ - "enumflags2", - "futures-channel", - "futures-util", - "rand 0.9.2", - "raw-window-handle", - "serde", - "serde_repr", - "tokio", - "url", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "zbus", -] - [[package]] name = "async-broadcast" version = "0.7.2" @@ -112,9 +82,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.3" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", @@ -144,9 +114,9 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ "event-listener", "event-listener-strategy", @@ -179,7 +149,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -214,7 +184,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -252,21 +222,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link 0.2.1", -] - [[package]] name = "base64" version = "0.21.7" @@ -279,6 +234,21 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -287,11 +257,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -303,22 +273,13 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block2" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" -dependencies = [ - "objc2 0.5.2", -] - [[package]] name = "block2" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "objc2 0.6.3", + "objc2", ] [[package]] @@ -357,15 +318,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytemuck" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" [[package]] name = "byteorder" @@ -375,9 +336,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" dependencies = [ "serde", ] @@ -388,7 +349,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "cairo-sys-rs", "glib", "libc", @@ -409,9 +370,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" dependencies = [ "serde_core", ] @@ -436,7 +397,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -446,14 +407,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" dependencies = [ "serde", - "toml 0.9.7", + "toml 0.9.12+spec-1.1.0", ] [[package]] name = "cc" -version = "1.2.40" +version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "shlex", @@ -488,21 +449,15 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -575,11 +530,11 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "core-foundation 0.10.1", "core-graphics-types", "foreign-types 0.5.0", @@ -592,7 +547,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "core-foundation 0.10.1", "libc", ] @@ -632,9 +587,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -657,6 +612,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.13.1", + "smallvec", +] + [[package]] name = "cssparser-macros" version = "0.6.1" @@ -664,7 +632,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -674,14 +642,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "darling" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ "darling_core", "darling_macro", @@ -689,34 +657,33 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "darling_macro" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "deranged" -version = "0.5.4" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", "serde_core", @@ -730,7 +697,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -743,7 +710,28 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.106", + "syn 2.0.117", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", ] [[package]] @@ -777,22 +765,16 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - [[package]] name = "dispatch2" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.9.4", - "block2 0.6.2", + "bitflags 2.11.0", + "block2", "libc", - "objc2 0.6.3", + "objc2", ] [[package]] @@ -803,23 +785,14 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "dlib" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" -dependencies = [ - "libloading", + "syn 2.0.117", ] [[package]] name = "dlopen2" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b54f373ccf864bf587a89e880fb7610f8d73f3045f13580948ccbcaff26febff" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" dependencies = [ "dlopen2_derive", "libc", @@ -829,20 +802,29 @@ dependencies = [ [[package]] name = "dlopen2_derive" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] -name = "downcast-rs" -version = "1.2.1" +name = "dom_query" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +checksum = "4d9c2e7f1d22d0f2ce07626d259b8a55f4a47cb0938d4006dd8ae037f17d585e" +dependencies = [ + "bit-set", + "cssparser 0.36.0", + "foldhash 0.2.0", + "html5ever 0.36.1", + "precomputed-hash", + "selectors 0.35.0", + "tendril", +] [[package]] name = "dpi" @@ -855,9 +837,9 @@ dependencies = [ [[package]] name = "dtoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" [[package]] name = "dtoa-short" @@ -882,14 +864,14 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "embed-resource" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e" +checksum = "47ec73ddcf6b7f23173d5c3c5a32b5507dc0a734de7730aa14abc5d5e296bb5f" dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.9.7", + "toml 0.9.12+spec-1.1.0", "vswhom", "winreg", ] @@ -911,9 +893,9 @@ dependencies = [ [[package]] name = "endi" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" [[package]] name = "enumflags2" @@ -933,7 +915,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -944,9 +926,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" dependencies = [ "serde", "serde_core", @@ -1011,27 +993,26 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.26" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.60.2", ] [[package]] name = "find-msvc-tools" -version = "0.1.3" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "flate2" -version = "1.1.4" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1043,6 +1024,18 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1070,7 +1063,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -1106,24 +1099,24 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1132,9 +1125,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -1151,32 +1144,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-io", @@ -1185,7 +1178,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1320,36 +1312,39 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", - "r-efi", - "wasi 0.14.7+wasi-0.2.4", - "wasm-bindgen", + "r-efi 5.3.0", + "wasip2", ] [[package]] -name = "gimli" -version = "0.32.3" +name = "getrandom" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] [[package]] name = "gio" @@ -1389,7 +1384,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "futures-channel", "futures-core", "futures-executor", @@ -1417,7 +1412,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -1496,14 +1491,14 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "h2" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" dependencies = [ "atomic-waker", "bytes", @@ -1511,7 +1506,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.11.4", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -1526,9 +1521,18 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heck" @@ -1562,18 +1566,27 @@ checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", - "markup5ever", + "markup5ever 0.14.1", "match_token", ] +[[package]] +name = "html5ever" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e" +dependencies = [ + "log", + "markup5ever 0.36.1", +] + [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1614,9 +1627,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -1648,7 +1661,6 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots", ] [[package]] @@ -1669,14 +1681,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -1695,9 +1706,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1719,9 +1730,9 @@ dependencies = [ [[package]] name = "ico" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" dependencies = [ "byteorder", "png", @@ -1729,9 +1740,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -1742,9 +1753,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -1755,11 +1766,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -1770,42 +1780,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -1813,6 +1819,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -1853,12 +1865,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -1872,28 +1884,17 @@ dependencies = [ "cfb", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -1920,9 +1921,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "javascriptcore-rs" @@ -1971,9 +1972,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -2007,7 +2008,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "serde", "unicode-segmentation", ] @@ -2018,17 +2019,17 @@ version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ - "cssparser", - "html5ever", - "indexmap 2.11.4", - "selectors", + "cssparser 0.29.6", + "html5ever 0.29.1", + "indexmap 2.13.0", + "selectors 0.24.0", ] [[package]] -name = "lazy_static" -version = "1.5.0" +name = "leb128fmt" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libappindicator" @@ -2056,9 +2057,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.176" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libloading" @@ -2072,26 +2073,27 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "libc", - "redox_syscall", + "plain", + "redox_syscall 0.7.3", ] [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "lock_api" @@ -2104,15 +2106,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" - -[[package]] -name = "lru-slab" -version = "0.1.2" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "mac" @@ -2129,9 +2125,20 @@ dependencies = [ "log", "phf 0.11.3", "phf_codegen 0.11.3", - "string_cache", - "string_cache_codegen", + "string_cache 0.8.9", + "string_cache_codegen 0.5.4", + "tendril", +] + +[[package]] +name = "markup5ever" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" +dependencies = [ + "log", "tendril", + "web_atoms", ] [[package]] @@ -2142,7 +2149,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -2153,9 +2160,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memoffset" @@ -2174,9 +2181,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minisign-verify" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e856fdd13623a2f5f2f54676a4ee49502a96a80ef4a62bcedd23d52427c44d43" +checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e" [[package]] name = "miniz_oxide" @@ -2190,13 +2197,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2209,22 +2216,22 @@ dependencies = [ "dpi", "gtk", "keyboard-types", - "objc2 0.6.3", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.2", + "objc2-foundation", "once_cell", "png", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", "windows-sys 0.60.2", ] [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", @@ -2243,7 +2250,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "jni-sys", "log", "ndk-sys", @@ -2273,19 +2280,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset", -] - [[package]] name = "nodrop" version = "0.1.14" @@ -2294,9 +2288,9 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-traits" @@ -2309,9 +2303,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", "rustversion", @@ -2319,83 +2313,37 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] -[[package]] -name = "objc-sys" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" - [[package]] name = "objc2" -version = "0.5.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ - "objc-sys", "objc2-encode", + "objc2-exception-helper", ] [[package]] -name = "objc2" -version = "0.6.3" +name = "objc2-app-kit" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "objc2-encode", - "objc2-exception-helper", -] - -[[package]] -name = "objc2-app-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" -dependencies = [ - "bitflags 2.9.4", - "block2 0.6.2", - "libc", - "objc2 0.6.3", - "objc2-cloud-kit", - "objc2-core-data", + "bitflags 2.11.0", + "block2", + "objc2", "objc2-core-foundation", - "objc2-core-graphics", - "objc2-core-image", - "objc2-core-text", - "objc2-core-video", - "objc2-foundation 0.3.2", - "objc2-quartz-core 0.3.2", -] - -[[package]] -name = "objc2-cloud-kit" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" -dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", - "objc2-foundation 0.3.2", -] - -[[package]] -name = "objc2-core-data" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" -dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", - "objc2-foundation 0.3.2", + "objc2-foundation", ] [[package]] @@ -2404,9 +2352,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "dispatch2", - "objc2 0.6.3", + "objc2", ] [[package]] @@ -2415,48 +2363,13 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "dispatch2", - "objc2 0.6.3", + "objc2", "objc2-core-foundation", "objc2-io-surface", ] -[[package]] -name = "objc2-core-image" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" -dependencies = [ - "objc2 0.6.3", - "objc2-foundation 0.3.2", -] - -[[package]] -name = "objc2-core-text" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" -dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", - "objc2-core-foundation", - "objc2-core-graphics", -] - -[[package]] -name = "objc2-core-video" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" -dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-io-surface", -] - [[package]] name = "objc2-encode" version = "4.1.0" @@ -2472,28 +2385,16 @@ dependencies = [ "cc", ] -[[package]] -name = "objc2-foundation" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" -dependencies = [ - "bitflags 2.9.4", - "block2 0.5.1", - "libc", - "objc2 0.5.2", -] - [[package]] name = "objc2-foundation" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.9.4", - "block2 0.6.2", + "bitflags 2.11.0", + "block2", "libc", - "objc2 0.6.3", + "objc2", "objc2-core-foundation", ] @@ -2503,56 +2404,21 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-javascript-core" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1e6550c4caed348956ce3370c9ffeca70bb1dbed4fa96112e7c6170e074586" -dependencies = [ - "objc2 0.6.3", + "bitflags 2.11.0", + "objc2", "objc2-core-foundation", ] -[[package]] -name = "objc2-metal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" -dependencies = [ - "bitflags 2.9.4", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", -] - [[package]] name = "objc2-osa-kit" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", + "bitflags 2.11.0", + "objc2", "objc2-app-kit", - "objc2-foundation 0.3.2", -] - -[[package]] -name = "objc2-quartz-core" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" -dependencies = [ - "bitflags 2.9.4", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-metal", + "objc2-foundation", ] [[package]] @@ -2561,20 +2427,10 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", - "objc2-foundation 0.3.2", -] - -[[package]] -name = "objc2-security" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a" -dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", + "bitflags 2.11.0", + "objc2", "objc2-core-foundation", + "objc2-foundation", ] [[package]] @@ -2583,10 +2439,10 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.3", + "bitflags 2.11.0", + "objc2", "objc2-core-foundation", - "objc2-foundation 0.3.2", + "objc2-foundation", ] [[package]] @@ -2595,36 +2451,25 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" dependencies = [ - "bitflags 2.9.4", - "block2 0.6.2", - "objc2 0.6.3", + "bitflags 2.11.0", + "block2", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.2", - "objc2-javascript-core", - "objc2-security", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", + "objc2-foundation", ] [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "open" -version = "5.3.2" +version = "5.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" dependencies = [ "dunce", "is-wsl", @@ -2634,11 +2479,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.74" +version = "0.10.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -2655,20 +2500,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.110" +version = "0.9.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" dependencies = [ "cc", "libc", @@ -2708,12 +2553,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" dependencies = [ - "objc2 0.6.3", - "objc2-foundation 0.3.2", + "objc2", + "objc2-foundation", "objc2-osa-kit", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2765,7 +2610,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", "windows-link 0.2.1", ] @@ -2812,6 +2657,17 @@ dependencies = [ "phf_shared 0.11.3", ] +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", + "serde", +] + [[package]] name = "phf_codegen" version = "0.8.0" @@ -2832,6 +2688,16 @@ dependencies = [ "phf_shared 0.11.3", ] +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", +] + [[package]] name = "phf_generator" version = "0.8.0" @@ -2862,6 +2728,16 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + [[package]] name = "phf_macros" version = "0.10.0" @@ -2886,7 +2762,20 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -2913,14 +2802,23 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher 1.0.1", + "siphasher 1.0.2", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher 1.0.2", ] [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pin-utils" @@ -2930,9 +2828,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", "fastrand", @@ -2945,6 +2843,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "plist" version = "1.8.0" @@ -2952,8 +2856,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", - "indexmap 2.11.4", - "quick-xml 0.38.3", + "indexmap 2.13.0", + "quick-xml", "serde", "time", ] @@ -2987,9 +2891,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -3015,6 +2919,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -3037,11 +2951,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.23.6", + "toml_edit 0.25.4+spec-1.1.0", ] [[package]] @@ -3076,9 +2990,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -3091,7 +3005,7 @@ dependencies = [ "chrono", "flate2", "futures-util", - "reqwest", + "reqwest 0.12.28", "serde", "serde_json", "sha2", @@ -3109,82 +3023,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" -dependencies = [ - "memchr", -] - -[[package]] -name = "quick-xml" -version = "0.38.3" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", ] -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.17", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "bytes", - "getrandom 0.3.3", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.17", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - [[package]] name = "quote" -version = "1.0.41" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -3195,6 +3045,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" version = "0.7.3" @@ -3220,16 +3076,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - [[package]] name = "rand_chacha" version = "0.2.2" @@ -3250,16 +3096,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - [[package]] name = "rand_core" version = "0.5.1" @@ -3275,16 +3111,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.3", + "getrandom 0.2.17", ] [[package]] @@ -3317,7 +3144,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", +] + +[[package]] +name = "redox_syscall" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +dependencies = [ + "bitflags 2.11.0", ] [[package]] @@ -3326,9 +3162,9 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -3348,14 +3184,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "regex" -version = "1.11.3" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -3365,9 +3201,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.11" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -3376,15 +3212,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" -version = "0.12.23" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", @@ -3405,8 +3241,6 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", - "quinn", - "rustls", "rustls-pki-types", "serde", "serde_json", @@ -3414,6 +3248,44 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams 0.4.2", + "web-sys", +] + +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", "tokio-rustls", "tokio-util", "tower", @@ -3422,34 +3294,32 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.5.0", "web-sys", - "webpki-roots", ] [[package]] name = "rfd" -version = "0.15.4" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" dependencies = [ - "ashpd", - "block2 0.6.2", + "block2", "dispatch2", "glib-sys", "gobject-sys", "gtk-sys", "js-sys", "log", - "objc2 0.6.3", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.2", + "objc2-foundation", "raw-window-handle", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3460,18 +3330,12 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -3489,11 +3353,11 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys", @@ -3502,9 +3366,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.31" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "once_cell", "ring", @@ -3514,21 +3378,59 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ - "web-time", "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ "ring", "rustls-pki-types", @@ -3543,9 +3445,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -3558,9 +3460,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] @@ -3594,9 +3496,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -3613,15 +3515,9 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.106", + "syn 2.0.117", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -3630,12 +3526,12 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.11.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.9.4", - "core-foundation 0.9.4", + "bitflags 2.11.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -3643,9 +3539,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -3658,14 +3554,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", - "cssparser", - "derive_more", + "cssparser 0.29.6", + "derive_more 0.99.20", "fxhash", "log", "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", - "servo_arc", + "servo_arc 0.2.0", + "smallvec", +] + +[[package]] +name = "selectors" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fdfed56cd634f04fe8b9ddf947ae3dc493483e819593d2ba17df9ad05db8b2" +dependencies = [ + "bitflags 2.11.0", + "cssparser 0.36.0", + "derive_more 2.1.1", + "log", + "new_debug_unreachable", + "phf 0.13.1", + "phf_codegen 0.13.1", + "precomputed-hash", + "rustc-hash", + "servo_arc 0.4.3", "smallvec", ] @@ -3718,7 +3633,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -3729,20 +3644,20 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -3753,7 +3668,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -3767,9 +3682,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ "serde_core", ] @@ -3788,17 +3703,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.15.0" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.4", + "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.0.4", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -3807,14 +3722,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.15.0" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -3836,7 +3751,7 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -3849,6 +3764,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha2" version = "0.10.9" @@ -3900,18 +3824,19 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "siphasher" @@ -3921,15 +3846,15 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -3939,34 +3864,34 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "softbuffer" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" dependencies = [ "bytemuck", - "cfg_aliases", - "core-graphics", - "foreign-types 0.5.0", "js-sys", - "log", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-quartz-core 0.2.2", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", "raw-window-handle", - "redox_syscall", + "redox_syscall 0.5.18", + "tracing", "wasm-bindgen", "web-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3997,15 +3922,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "string_cache" @@ -4020,6 +3939,18 @@ dependencies = [ "serde", ] +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.13.1", + "precomputed-hash", +] + [[package]] name = "string_cache_codegen" version = "0.5.4" @@ -4032,6 +3963,18 @@ dependencies = [ "quote", ] +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", +] + [[package]] name = "strsim" version = "0.11.1" @@ -4068,9 +4011,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -4094,16 +4037,16 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "system-configuration" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -4133,35 +4076,33 @@ dependencies = [ [[package]] name = "tao" -version = "0.34.3" +version = "0.34.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "959469667dbcea91e5485fc48ba7dd6023face91bb0f1a14681a70f99847c3f7" +checksum = "6e06d52c379e63da659a483a958110bbde891695a0ecb53e48cc7786d5eda7bb" dependencies = [ - "bitflags 2.9.4", - "block2 0.6.2", + "bitflags 2.11.0", + "block2", "core-foundation 0.10.1", "core-graphics", "crossbeam-channel", - "dispatch", + "dispatch2", "dlopen2", "dpi", "gdkwayland-sys", "gdkx11-sys", "gtk", "jni", - "lazy_static", "libc", "log", "ndk", "ndk-context", "ndk-sys", - "objc2 0.6.3", + "objc2", "objc2-app-kit", - "objc2-foundation 0.3.2", + "objc2-foundation", "once_cell", "parking_lot", "raw-window-handle", - "scopeguard", "tao-macros", "unicode-segmentation", "url", @@ -4179,7 +4120,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -4201,9 +4142,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.8.5" +version = "2.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d1d3b3dc4c101ac989fd7db77e045cc6d91a25349cd410455cb5c57d510c1c" +checksum = "da77cc00fb9028caf5b5d4650f75e31f1ef3693459dfca7f7e506d1ecef0ba2d" dependencies = [ "anyhow", "bytes", @@ -4211,7 +4152,7 @@ dependencies = [ "dirs", "dunce", "embed_plist", - "getrandom 0.3.3", + "getrandom 0.3.4", "glob", "gtk", "heck 0.5.0", @@ -4222,15 +4163,15 @@ dependencies = [ "log", "mime", "muda", - "objc2 0.6.3", + "objc2", "objc2-app-kit", - "objc2-foundation 0.3.2", + "objc2-foundation", "objc2-ui-kit", "objc2-web-kit", "percent-encoding", "plist", "raw-window-handle", - "reqwest", + "reqwest 0.13.2", "serde", "serde_json", "serde_repr", @@ -4241,11 +4182,10 @@ dependencies = [ "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tray-icon", "url", - "urlpattern", "webkit2gtk", "webview2-com", "window-vibrancy", @@ -4254,9 +4194,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.4.1" +version = "2.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c432ccc9ff661803dab74c6cd78de11026a578a9307610bbc39d3c55be7943f" +checksum = "4bbc990d1dbf57a8e1c7fa2327f2a614d8b757805603c1b9ba5c81bade09fd4d" dependencies = [ "anyhow", "cargo_toml", @@ -4270,15 +4210,15 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.9.7", + "toml 0.9.12+spec-1.1.0", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.4.0" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a" +checksum = "d4a24476afd977c5d5d169f72425868613d82747916dd29e0a357c84c4bd6d29" dependencies = [ "base64 0.22.1", "brotli", @@ -4292,9 +4232,9 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.106", + "syn 2.0.117", "tauri-utils", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", "url", "uuid", @@ -4303,23 +4243,23 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.4.0" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e" +checksum = "d39b349a98dadaffebb73f0a40dcd1f23c999211e5a2e744403db384d0c33de7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "tauri-codegen", "tauri-utils", ] [[package]] name = "tauri-plugin" -version = "2.4.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9946a3cede302eac0c6eb6c6070ac47b1768e326092d32efbb91f21ed58d978f" +checksum = "ddde7d51c907b940fb573006cdda9a642d6a7c8153657e88f8a5c3c9290cd4aa" dependencies = [ "anyhow", "glob", @@ -4328,15 +4268,15 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.9.7", + "toml 0.9.12+spec-1.1.0", "walkdir", ] [[package]] name = "tauri-plugin-dialog" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0beee42a4002bc695550599b011728d9dfabf82f767f134754ed6655e434824e" +checksum = "9204b425d9be8d12aa60c2a83a289cf7d1caae40f57f336ed1155b3a5c0e359b" dependencies = [ "log", "raw-window-handle", @@ -4346,15 +4286,15 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-plugin-fs", - "thiserror 2.0.17", + "thiserror 2.0.18", "url", ] [[package]] name = "tauri-plugin-fs" -version = "2.4.2" +version = "2.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "315784ec4be45e90a987687bae7235e6be3d6e9e350d2b75c16b8a4bf22c1db7" +checksum = "ed390cc669f937afeb8b28032ce837bac8ea023d975a2e207375ec05afaf1804" dependencies = [ "anyhow", "dunce", @@ -4367,28 +4307,28 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-utils", - "thiserror 2.0.17", - "toml 0.9.7", + "thiserror 2.0.18", + "toml 0.9.12+spec-1.1.0", "url", ] [[package]] name = "tauri-plugin-opener" -version = "2.5.0" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786156aa8e89e03d271fbd3fe642207da8e65f3c961baa9e2930f332bf80a1f5" +checksum = "fc624469b06f59f5a29f874bbc61a2ed737c0f9c23ef09855a292c389c42e83f" dependencies = [ "dunce", "glob", "objc2-app-kit", - "objc2-foundation 0.3.2", + "objc2-foundation", "open", "schemars 0.8.22", "serde", "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.17", + "thiserror 2.0.18", "url", "windows", "zbus", @@ -4396,9 +4336,9 @@ dependencies = [ [[package]] name = "tauri-plugin-process" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7461c622a5ea00eb9cd9f7a08dbd3bf79484499fd5c21aa2964677f64ca651ab" +checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a" dependencies = [ "tauri", "tauri-plugin", @@ -4406,9 +4346,9 @@ dependencies = [ [[package]] name = "tauri-plugin-shell" -version = "2.3.1" +version = "2.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54777d0c0d8add34eea3ced84378619ef5b97996bd967d3038c668feefd21071" +checksum = "8457dbf9e2bab1edd8df22bb2c20857a59a9868e79cb3eac5ed639eec4d0c73b" dependencies = [ "encoding_rs", "log", @@ -4421,15 +4361,15 @@ dependencies = [ "shared_child", "tauri", "tauri-plugin", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", ] [[package]] name = "tauri-plugin-updater" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27cbc31740f4d507712550694749572ec0e43bdd66992db7599b89fbfd6b167b" +checksum = "3fe8e9bebd88fc222938ffdfbdcfa0307081423bd01e3252fc337d8bde81fc61" dependencies = [ "base64 0.22.1", "dirs", @@ -4441,7 +4381,8 @@ dependencies = [ "minisign-verify", "osakit", "percent-encoding", - "reqwest", + "reqwest 0.13.2", + "rustls", "semver", "serde", "serde_json", @@ -4449,7 +4390,7 @@ dependencies = [ "tauri", "tauri-plugin", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", "tokio", "url", @@ -4459,23 +4400,23 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.8.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846" +checksum = "2826d79a3297ed08cd6ea7f412644ef58e32969504bc4fbd8d7dbeabc4445ea2" dependencies = [ "cookie", "dpi", "gtk", "http", "jni", - "objc2 0.6.3", + "objc2", "objc2-ui-kit", "objc2-web-kit", "raw-window-handle", "serde", "serde_json", "tauri-utils", - "thiserror 2.0.17", + "thiserror 2.0.18", "url", "webkit2gtk", "webview2-com", @@ -4484,17 +4425,16 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.8.1" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fe9d48bd122ff002064e88cfcd7027090d789c4302714e68fcccba0f4b7807" +checksum = "e11ea2e6f801d275fdd890d6c9603736012742a1c33b96d0db788c9cdebf7f9e" dependencies = [ "gtk", "http", "jni", "log", - "objc2 0.6.3", + "objc2", "objc2-app-kit", - "objc2-foundation 0.3.2", "once_cell", "percent-encoding", "raw-window-handle", @@ -4511,9 +4451,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.7.0" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212" +checksum = "219a1f983a2af3653f75b5747f76733b0da7ff03069c7a41901a5eb3ace4557d" dependencies = [ "anyhow", "brotli", @@ -4521,7 +4461,7 @@ dependencies = [ "ctor", "dunce", "glob", - "html5ever", + "html5ever 0.29.1", "http", "infer", "json-patch", @@ -4539,8 +4479,8 @@ dependencies = [ "serde_json", "serde_with", "swift-rs", - "thiserror 2.0.17", - "toml 0.9.7", + "thiserror 2.0.18", + "toml 0.9.12+spec-1.1.0", "url", "urlpattern", "uuid", @@ -4549,22 +4489,23 @@ dependencies = [ [[package]] name = "tauri-winres" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd21509dd1fa9bd355dc29894a6ff10635880732396aa38c0066c1e6c1ab8074" +checksum = "1087b111fe2b005e42dbdc1990fc18593234238d47453b0c99b7de1c9ab2c1e0" dependencies = [ + "dunce", "embed-resource", - "toml 0.9.7", + "toml 0.9.12+spec-1.1.0", ] [[package]] name = "tempfile" -version = "3.23.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.4.2", "once_cell", "rustix", "windows-sys 0.61.2", @@ -4592,11 +4533,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -4607,46 +4548,46 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "time" -version = "0.3.44" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -4654,46 +4595,27 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", ] -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.47.1" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "slab", "socket2", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4708,9 +4630,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -4718,9 +4640,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -4743,17 +4665,17 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.7" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.13.0", "serde_core", - "serde_spanned 1.0.2", - "toml_datetime 0.7.2", + "serde_spanned 1.0.4", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 0.7.13", + "winnow 0.7.15", ] [[package]] @@ -4767,9 +4689,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.2" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" dependencies = [ "serde_core", ] @@ -4780,7 +4711,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.13.0", "toml_datetime 0.6.3", "winnow 0.5.40", ] @@ -4791,7 +4722,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.13.0", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.3", @@ -4800,36 +4731,36 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.6" +version = "0.25.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" +checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" dependencies = [ - "indexmap 2.11.4", - "toml_datetime 0.7.2", + "indexmap 2.13.0", + "toml_datetime 1.0.0+spec-1.1.0", "toml_parser", - "winnow 0.7.13", + "winnow 0.7.15", ] [[package]] name = "toml_parser" -version = "1.0.3" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ - "winnow 0.7.13", + "winnow 0.7.15", ] [[package]] name = "toml_writer" -version = "1.0.3" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -4842,11 +4773,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.11.0", "bytes", "futures-util", "http", @@ -4872,9 +4803,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -4883,44 +4814,44 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] [[package]] name = "tray-icon" -version = "0.21.1" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d92153331e7d02ec09137538996a7786fe679c629c279e82a6be762b7e6fe2" +checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c" dependencies = [ "crossbeam-channel", "dirs", "libappindicator", "muda", - "objc2 0.6.3", + "objc2", "objc2-app-kit", "objc2-core-foundation", "objc2-core-graphics", - "objc2-foundation 0.3.2", + "objc2-foundation", "once_cell", "png", "serde", - "thiserror 2.0.17", - "windows-sys 0.59.0", + "thiserror 2.0.18", + "windows-sys 0.60.2", ] [[package]] @@ -4943,13 +4874,13 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "uds_windows" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -4995,9 +4926,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" @@ -5005,6 +4936,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "untrusted" version = "0.9.0" @@ -5013,14 +4950,15 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -5049,13 +4987,13 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.18.1" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.4.2", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] @@ -5067,9 +5005,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version-compare" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" [[package]] name = "version_check" @@ -5129,28 +5067,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wasip2", + "wit-bindgen", ] [[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", @@ -5159,27 +5097,14 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.54" +version = "0.4.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -5188,9 +5113,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5198,124 +5123,113 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.106", - "wasm-bindgen-backend", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] [[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wayland-backend" -version = "0.3.11" +name = "wasm-encoder" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" dependencies = [ - "cc", - "downcast-rs", - "rustix", - "scoped-tls", - "smallvec", - "wayland-sys", + "leb128fmt", + "wasmparser", ] [[package]] -name = "wayland-client" -version = "0.31.11" +name = "wasm-metadata" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ - "bitflags 2.9.4", - "rustix", - "wayland-backend", - "wayland-scanner", + "anyhow", + "indexmap 2.13.0", + "wasm-encoder", + "wasmparser", ] [[package]] -name = "wayland-protocols" -version = "0.32.9" +name = "wasm-streams" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ - "bitflags 2.9.4", - "wayland-backend", - "wayland-client", - "wayland-scanner", + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] -name = "wayland-scanner" -version = "0.31.7" +name = "wasm-streams" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" dependencies = [ - "proc-macro2", - "quick-xml 0.37.5", - "quote", + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] -name = "wayland-sys" -version = "0.31.7" +name = "wasmparser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "dlib", - "log", - "pkg-config", + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap 2.13.0", + "semver", ] [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "web-time" -version = "1.1.0" +name = "web_atoms" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" dependencies = [ - "js-sys", - "wasm-bindgen", + "phf 0.13.1", + "phf_codegen 0.13.1", + "string_cache 0.9.0", + "string_cache_codegen 0.6.1", ] [[package]] name = "webkit2gtk" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -5337,9 +5251,9 @@ dependencies = [ [[package]] name = "webkit2gtk-sys" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" dependencies = [ "bitflags 1.3.2", "cairo-sys-rs", @@ -5356,19 +5270,19 @@ dependencies = [ ] [[package]] -name = "webpki-roots" -version = "1.0.4" +name = "webpki-root-certs" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" dependencies = [ "rustls-pki-types", ] [[package]] name = "webview2-com" -version = "0.38.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" dependencies = [ "webview2-com-macros", "webview2-com-sys", @@ -5380,22 +5294,22 @@ dependencies = [ [[package]] name = "webview2-com-macros" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] name = "webview2-com-sys" -version = "0.38.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" dependencies = [ - "thiserror 2.0.17", + "thiserror 2.0.18", "windows", "windows-core 0.61.2", ] @@ -5437,10 +5351,10 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "objc2 0.6.3", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.2", + "objc2-foundation", "raw-window-handle", "windows-sys 0.59.0", "windows-version", @@ -5513,7 +5427,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -5524,7 +5438,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -5551,13 +5465,13 @@ dependencies = [ [[package]] name = "windows-registry" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -5856,9 +5770,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" dependencies = [ "memchr", ] @@ -5875,42 +5789,123 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap 2.13.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wry" -version = "0.53.4" +version = "0.54.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d78ec082b80fa088569a970d043bb3050abaabf4454101d44514ee8d9a8c9f6" +checksum = "a24eda84b5d488f99344e54b807138896cee8df0b2d16c793f1f6b80e6d8df1f" dependencies = [ "base64 0.22.1", - "block2 0.6.2", + "block2", "cookie", "crossbeam-channel", "dirs", + "dom_query", "dpi", "dunce", "gdkx11", "gtk", - "html5ever", "http", "javascriptcore-rs", "jni", - "kuchikiki", "libc", "ndk", - "objc2 0.6.3", + "objc2", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.2", + "objc2-foundation", "objc2-ui-kit", "objc2-web-kit", "once_cell", @@ -5919,7 +5914,7 @@ dependencies = [ "sha2", "soup3", "tao-macros", - "thiserror 2.0.17", + "thiserror 2.0.18", "url", "webkit2gtk", "webkit2gtk-sys", @@ -5963,11 +5958,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -5975,21 +5969,21 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "synstructure", ] [[package]] name = "zbus" -version = "5.11.0" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d07e46d035fb8e375b2ce63ba4e4ff90a7f73cf2ffb0138b29e1158d2eaadf7" +checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" dependencies = [ "async-broadcast", "async-executor", @@ -6005,15 +5999,16 @@ dependencies = [ "futures-core", "futures-lite", "hex", - "nix", + "libc", "ordered-stream", + "rustix", "serde", "serde_repr", - "tokio", "tracing", "uds_windows", - "windows-sys 0.60.2", - "winnow 0.7.13", + "uuid", + "windows-sys 0.61.2", + "winnow 0.7.15", "zbus_macros", "zbus_names", "zvariant", @@ -6021,14 +6016,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.11.0" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e797a9c847ed3ccc5b6254e8bcce056494b375b511b3d6edcec0aeb4defaca" +checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "zbus_names", "zvariant", "zvariant_utils", @@ -6036,34 +6031,33 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.2.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" dependencies = [ "serde", - "static_assertions", - "winnow 0.7.13", + "winnow 0.7.15", "zvariant", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -6083,21 +6077,21 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -6106,9 +6100,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -6117,13 +6111,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", ] [[package]] @@ -6134,47 +6128,52 @@ checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" dependencies = [ "arbitrary", "crc32fast", - "indexmap 2.11.4", + "indexmap 2.13.0", "memchr", ] +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + [[package]] name = "zvariant" -version = "5.7.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" +checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" dependencies = [ "endi", "enumflags2", "serde", - "url", - "winnow 0.7.13", + "winnow 0.7.15", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.7.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" +checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" dependencies = [ - "proc-macro-crate 3.4.0", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.117", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.106", - "winnow 0.7.13", + "syn 2.0.117", + "winnow 0.7.15", ] diff --git a/qubes-gui/src-tauri/Cargo.toml b/qubes-gui/src-tauri/Cargo.toml index 1cc6b26..818c920 100644 --- a/qubes-gui/src-tauri/Cargo.toml +++ b/qubes-gui/src-tauri/Cargo.toml @@ -15,11 +15,11 @@ name = "qubes_gui_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] -tauri-build = { version = "2.4", features = [] } +tauri-build = { version = "2", features = [] } [dependencies] -tauri = { version = "2.4", features = ["protocol-asset", "devtools"] } -tauri-plugin-opener = "2.5" +tauri = { version = "2", features = ["protocol-asset", "devtools"] } +tauri-plugin-opener = "2" tauri-plugin-process = "2" tauri-plugin-dialog = "2" tauri-plugin-fs = "2" @@ -27,7 +27,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" chrono = "0.4" base64 = "0.22" -tauri-plugin-shell = "2.3.1" +tauri-plugin-shell = "2" tauri-plugin-updater = "2" reqwest = { version = "0.12", features = ["json", "stream"] } tokio = { version = "1", features = ["process", "sync", "time"] } From 3d3701a956a4be1f32089f9fd61ffe4200683173 Mon Sep 17 00:00:00 2001 From: ABLA Date: Mon, 16 Mar 2026 21:12:31 +0300 Subject: [PATCH 05/24] fix: add torch distributed/futures/rpc stubs to fix TTS and GPU acceleration The PyInstaller bundle strips torch.distributed, torch.futures, torch.rpc and their submodules. TTS (Kokoro) and GPU acceleration code paths import these at runtime, causing startup failures: - No module named 'torch.distributed' - cannot import name 'Future' from 'torch.futures' - cannot import name 'nn' from partially initialized module 'torch' Fixes: - torch/distributed/__init__.py: import hook that auto-stubs any torch.distributed.* submodule import; pre-builds Join/Joinable/JoinHook as real classes so nn.parallel.distributed can subclass them - torch/futures/__init__.py: minimal Future class with full interface - torch/rpc/__init__.py: stub - torch/nn/__init__.py: pre-import functional+init before modules to break circular import chain Apply patches/ files to D:\Qubes\qubes-backend\_internal\torch\ after any Qubes update that overwrites the bundle. Co-Authored-By: Claude Sonnet 4.6 --- patches/README.md | 21 ++++ patches/torch/distributed/__init__.py | 169 ++++++++++++++++++++++++++ patches/torch/futures/__init__.py | 89 ++++++++++++++ patches/torch/nn/__init__.py | 66 ++++++++++ patches/torch/rpc/__init__.py | 2 + 5 files changed, 347 insertions(+) create mode 100644 patches/README.md create mode 100644 patches/torch/distributed/__init__.py create mode 100644 patches/torch/futures/__init__.py create mode 100644 patches/torch/nn/__init__.py create mode 100644 patches/torch/rpc/__init__.py diff --git a/patches/README.md b/patches/README.md new file mode 100644 index 0000000..f952e33 --- /dev/null +++ b/patches/README.md @@ -0,0 +1,21 @@ +# Patches + +These files patch the PyInstaller bundle at `D:\Qubes\qubes-backend\_internal\`. + +## torch/ + +Fixes for missing torch submodules in the bundled build (torch 2.10.0+cpu). The PyInstaller +bundle strips `torch.distributed`, `torch.futures`, and `torch.rpc` — but TTS (Kokoro) and +GPU acceleration code paths import them. Without these patches, Qubes errors on: +- `No module named 'torch.distributed'` +- `cannot import name 'Future' from 'torch.futures'` +- `cannot import name 'nn' from partially initialized module 'torch'` + +### How to apply after a Qubes update: +Copy these files into `D:\Qubes\qubes-backend\_internal\torch\`: +``` +patches/torch/distributed/__init__.py → _internal/torch/distributed/__init__.py +patches/torch/futures/__init__.py → _internal/torch/futures/__init__.py +patches/torch/rpc/__init__.py → _internal/torch/rpc/__init__.py +patches/torch/nn/__init__.py → _internal/torch/nn/__init__.py (replaces existing) +``` diff --git a/patches/torch/distributed/__init__.py b/patches/torch/distributed/__init__.py new file mode 100644 index 0000000..2914ac1 --- /dev/null +++ b/patches/torch/distributed/__init__.py @@ -0,0 +1,169 @@ +""" +torch.distributed stub with auto-submodule import hook. + +Intercepts ANY `import torch.distributed.*` and returns a stub module so code +that imports distributed submodules doesn't crash in this single-machine build. +""" +import sys +import types + + +def is_available() -> bool: + return False + + +def is_initialized() -> bool: + return False + + +def is_nccl_available() -> bool: + return False + + +def is_gloo_available() -> bool: + return False + + +def is_mpi_available() -> bool: + return False + + +def init_process_group(*args, **kwargs) -> None: + pass + + +def get_rank(*args, **kwargs) -> int: + return 0 + + +def get_world_size(*args, **kwargs) -> int: + return 1 + + +def barrier(*args, **kwargs) -> None: + pass + + +def all_reduce(*args, **kwargs): + pass + + +def broadcast(*args, **kwargs): + pass + + +def destroy_process_group(*args, **kwargs) -> None: + pass + + +def new_group(*args, **kwargs): + return None + + +class ReduceOp: + SUM = 0 + PRODUCT = 1 + MIN = 2 + MAX = 3 + BAND = 4 + BOR = 5 + BXOR = 6 + + +Backend = None +GroupMember = None +group = None + + +def _make_stub_class(name: str) -> type: + """Create a stub class that can be used as a base class and has is_available().""" + return type(name, (object,), { + '__init__': lambda self, *a, **kw: None, + 'is_available': staticmethod(lambda: False), + 'is_initialized': staticmethod(lambda: False), + }) + + +class _StubModule(types.ModuleType): + """ + A module stub that returns stub classes for any attribute access. + Returning classes (not plain functions) ensures code can: + - Use attributes as base classes: class Foo(Joinable): ... + - Call methods on them: dist.rpc.is_available() + """ + + def __init__(self, name: str) -> None: + super().__init__(name) + self.__package__ = name + self.__path__: list = [] # makes Python treat it as a package + self.__spec__ = None + self._stub_cache: dict = {} + + def __getattr__(self, name: str): + if name.startswith('__'): + raise AttributeError(name) + if name not in self.__dict__.get('_stub_cache', {}): + stub = _make_stub_class(name) + self.__dict__.setdefault('_stub_cache', {})[name] = stub + return self.__dict__['_stub_cache'][name] + + +# Pre-build rpc stub with is_available so `dist.rpc.is_available()` works +rpc = _StubModule('torch.distributed.rpc') +rpc.is_available = lambda: False # type: ignore[attr-defined] +sys.modules['torch.distributed.rpc'] = rpc + +# Pre-build other common submodules +for _name in ('distributed_c10d', 'autograd', 'utils', 'algorithms', + 'fsdp', 'optim', 'elastic', 'checkpoint', 'tensor', '_tensor'): + _mod = _StubModule(f'torch.distributed.{_name}') + sys.modules[f'torch.distributed.{_name}'] = _mod + +# Pre-build torch.distributed.algorithms.join (used by nn/parallel/distributed.py) +_algorithms = sys.modules.get('torch.distributed.algorithms', _StubModule('torch.distributed.algorithms')) +_join_mod = _StubModule('torch.distributed.algorithms.join') + + +class Join(_make_stub_class('Join')): + pass + + +class Joinable(_make_stub_class('Joinable')): + pass + + +class JoinHook(_make_stub_class('JoinHook')): + pass + + +_join_mod.Join = Join # type: ignore[attr-defined] +_join_mod.Joinable = Joinable # type: ignore[attr-defined] +_join_mod.JoinHook = JoinHook # type: ignore[attr-defined] +sys.modules['torch.distributed.algorithms.join'] = _join_mod +sys.modules['torch.distributed.algorithms'] = _algorithms + + +class _DistributedImporter: + """ + Meta path finder that intercepts any `torch.distributed.*` import + and returns a _StubModule, preventing ModuleNotFoundError. + """ + + PREFIX = "torch.distributed." + + def find_module(self, fullname: str, path=None): + if fullname.startswith(self.PREFIX): + return self + return None + + def load_module(self, fullname: str): + if fullname in sys.modules: + return sys.modules[fullname] + mod = _StubModule(fullname) + sys.modules[fullname] = mod + return mod + + +# Install the import hook +if not any(isinstance(f, _DistributedImporter) for f in sys.meta_path): + sys.meta_path.append(_DistributedImporter()) diff --git a/patches/torch/futures/__init__.py b/patches/torch/futures/__init__.py new file mode 100644 index 0000000..021bfc1 --- /dev/null +++ b/patches/torch/futures/__init__.py @@ -0,0 +1,89 @@ +""" +torch.futures stub — provides a minimal Future class so TTS/model code +that does `from torch.futures import Future` doesn't crash. + +This file will be replaced by the real module if the update package contains it. +""" +import threading +from typing import Any, Callable, Generic, List, Optional, TypeVar + +T = TypeVar("T") + + +class Future(Generic[T]): + """Minimal stub for torch.futures.Future.""" + + def __init__(self) -> None: + self._result: Optional[Any] = None + self._exception: Optional[BaseException] = None + self._done = False + self._callbacks: List[Callable] = [] + self._lock = threading.Lock() + self._event = threading.Event() + + def done(self) -> bool: + return self._done + + def result(self) -> T: + self._event.wait() + if self._exception is not None: + raise self._exception + return self._result # type: ignore[return-value] + + def set_result(self, result: T) -> None: + with self._lock: + self._result = result + self._done = True + self._event.set() + for cb in self._callbacks: + try: + cb(self) + except Exception: + pass + + def set_exception(self, exception: BaseException) -> None: + with self._lock: + self._exception = exception + self._done = True + self._event.set() + + def add_done_callback(self, callback: Callable) -> None: + with self._lock: + if self._done: + callback(self) + else: + self._callbacks.append(callback) + + def wait(self) -> "Future[T]": + self._event.wait() + return self + + def then(self, callback: Callable) -> "Future": + fut: Future = Future() + def _cb(f: "Future") -> None: + try: + fut.set_result(callback(f)) + except Exception as e: + fut.set_exception(e) + self.add_done_callback(_cb) + return fut + + +def collect_all(futures: List[Future]) -> Future: + """Wait for all futures and return a future of list of results.""" + result_future: Future = Future() + if not futures: + result_future.set_result([]) + return result_future + results = [None] * len(futures) + remaining = [len(futures)] + lock = threading.Lock() + for i, f in enumerate(futures): + def _cb(fut, idx=i): + with lock: + results[idx] = fut.result() + remaining[0] -= 1 + if remaining[0] == 0: + result_future.set_result(results) + f.add_done_callback(_cb) + return result_future diff --git a/patches/torch/nn/__init__.py b/patches/torch/nn/__init__.py new file mode 100644 index 0000000..e2be695 --- /dev/null +++ b/patches/torch/nn/__init__.py @@ -0,0 +1,66 @@ +# mypy: allow-untyped-defs +from torch.nn.parameter import ( # usort: skip + Buffer as Buffer, + Parameter as Parameter, + UninitializedBuffer as UninitializedBuffer, + UninitializedParameter as UninitializedParameter, +) +# Pre-import functional and init so submodules can do `import torch.nn.functional as F` +# without hitting a circular import during torch.nn initialization +import torch.nn.functional # noqa: E402 +import torch.nn.init # noqa: E402 +from torch.nn.modules import * # usort: skip # noqa: F403 +from torch.nn import ( + attention as attention, + functional as functional, + init as init, + modules as modules, + parallel as parallel, + parameter as parameter, + utils as utils, +) +from torch.nn.parallel import DataParallel as DataParallel + + +def factory_kwargs(kwargs): + r"""Return a canonicalized dict of factory kwargs. + + Given kwargs, returns a canonicalized dict of factory kwargs that can be directly passed + to factory functions like torch.empty, or errors if unrecognized kwargs are present. + + This function makes it simple to write code like this:: + + class MyModule(nn.Module): + def __init__(self, **kwargs): + factory_kwargs = torch.nn.factory_kwargs(kwargs) + self.weight = Parameter(torch.empty(10, **factory_kwargs)) + + Why should you use this function instead of just passing `kwargs` along directly? + + 1. This function does error validation, so if there are unexpected kwargs we will + immediately report an error, instead of deferring it to the factory call + 2. This function supports a special `factory_kwargs` argument, which can be used to + explicitly specify a kwarg to be used for factory functions, in the event one of the + factory kwargs conflicts with an already existing argument in the signature (e.g. + in the signature ``def f(dtype, **kwargs)``, you can specify ``dtype`` for factory + functions, as distinct from the dtype argument, by saying + ``f(dtype1, factory_kwargs={"dtype": dtype2})``) + """ + if kwargs is None: + return {} + simple_keys = {"device", "dtype", "memory_format"} + expected_keys = simple_keys | {"factory_kwargs"} + if not kwargs.keys() <= expected_keys: + raise TypeError(f"unexpected kwargs {kwargs.keys() - expected_keys}") + + # guarantee no input kwargs is untouched + r = dict(kwargs.get("factory_kwargs", {})) + for k in simple_keys: + if k in kwargs: + if k in r: + raise TypeError( + f"{k} specified twice, in **kwargs and in factory_kwargs" + ) + r[k] = kwargs[k] + + return r diff --git a/patches/torch/rpc/__init__.py b/patches/torch/rpc/__init__.py new file mode 100644 index 0000000..9c6a379 --- /dev/null +++ b/patches/torch/rpc/__init__.py @@ -0,0 +1,2 @@ +# Stub: torch.rpc is not available in this bundled build +is_available = lambda: False From 23e00069209cbc24374307077c0c974a05910a83 Mon Sep 17 00:00:00 2001 From: ABLA Date: Mon, 16 Mar 2026 21:34:57 +0300 Subject: [PATCH 06/24] v0.9.1: fix TTS model updates, add AI model download, balance widget layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sidecar_server.py: add _handle_check_local_tts_models and _handle_update_local_tts_models handlers + POSITIONAL_ARG_NAMES entries. Fixes "Unknown command: update-local-tts-models (no method on GUIBridge)" error — commands now route through sidecar server instead of stale main() elif chain. - SettingsTab.tsx: add Download New Model input (model:tag + Download button) below Ollama model list. Reuses existing handlePullModel / pullProgress state. - QubeManagerTab.tsx: move Total Balance widget after "+ New Qube" button; restructure widget with label+refresh row, balance row, full-width Sweep All button. - Bump version 0.9.0 → 0.9.1 across package.json, tauri.conf.json, Cargo.toml. Co-Authored-By: Claude Sonnet 4.6 --- qubes-gui/package.json | 2 +- qubes-gui/src-tauri/Cargo.toml | 2 +- qubes-gui/src-tauri/tauri.conf.json | 2 +- .../src/components/tabs/QubeManagerTab.tsx | 43 ++++++++------ qubes-gui/src/components/tabs/SettingsTab.tsx | 24 ++++++++ sidecar_server.py | 59 +++++++++++++++++++ 6 files changed, 110 insertions(+), 22 deletions(-) diff --git a/qubes-gui/package.json b/qubes-gui/package.json index f30e56b..dd4c971 100644 --- a/qubes-gui/package.json +++ b/qubes-gui/package.json @@ -1,7 +1,7 @@ { "name": "qubes-gui", "private": true, - "version": "0.9.0", + "version": "0.9.1", "type": "module", "scripts": { "dev": "vite", diff --git a/qubes-gui/src-tauri/Cargo.toml b/qubes-gui/src-tauri/Cargo.toml index 818c920..6148c2c 100644 --- a/qubes-gui/src-tauri/Cargo.toml +++ b/qubes-gui/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qubes-gui" -version = "0.8.1" +version = "0.9.1" description = "A Tauri App" authors = ["you"] edition = "2021" diff --git a/qubes-gui/src-tauri/tauri.conf.json b/qubes-gui/src-tauri/tauri.conf.json index daffcc6..0646bf4 100644 --- a/qubes-gui/src-tauri/tauri.conf.json +++ b/qubes-gui/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Qubes", - "version": "0.9.0", + "version": "0.9.1", "identifier": "cash.qube.app", "plugins": { "updater": { diff --git a/qubes-gui/src/components/tabs/QubeManagerTab.tsx b/qubes-gui/src/components/tabs/QubeManagerTab.tsx index 63ddc6c..037e0ca 100644 --- a/qubes-gui/src/components/tabs/QubeManagerTab.tsx +++ b/qubes-gui/src/components/tabs/QubeManagerTab.tsx @@ -923,43 +923,52 @@ export const QubeManagerTab: React.FC = ({ > Restore - {/* Total Balance Widget */} -
-
+ + + New Qube + + + {/* Total Balance Widget — after New Qube */} +
+ {/* Top row: label + refresh */} +
Total Balance + +
+ {/* Balance amount */} +
{isRefreshingAll ? '...' : `${(totalSats / 1e8).toFixed(8)} BCH`}
- + {/* Sweep row — below balance */} {!showSweepAllModal ? ( ) : ( -
+
setSweepAllAddress(e.target.value)} placeholder="bitcoincash:q..." - className="bg-transparent text-[10px] text-text-primary placeholder:text-text-disabled font-mono focus:outline-none w-40" + className="bg-transparent text-[10px] text-text-primary placeholder:text-text-disabled font-mono focus:outline-none flex-1 min-w-0" autoFocus />
)} - - - + New Qube -
diff --git a/qubes-gui/src/components/tabs/SettingsTab.tsx b/qubes-gui/src/components/tabs/SettingsTab.tsx index 82ab78f..5b880ca 100644 --- a/qubes-gui/src/components/tabs/SettingsTab.tsx +++ b/qubes-gui/src/components/tabs/SettingsTab.tsx @@ -302,6 +302,7 @@ export const SettingsTab: React.FC = () => { const [ollamaModels, setOllamaModels] = useState([]); const [isPullingModel, setIsPullingModel] = useState(null); const [pullProgress, setPullProgress] = useState<{ status: string; completed?: number; total?: number } | null>(null); + const [newModelInput, setNewModelInput] = useState(''); const [ttsModelStatus, setTtsModelStatus] = useState<{ kokoro_installed: boolean; sentence_transformers_installed: boolean; @@ -1959,6 +1960,29 @@ export const SettingsTab: React.FC = () => { )}
)} + + {/* Download new model */} +
+

Download a new model (e.g. llama3.2:3b)

+
+ setNewModelInput(e.target.value)} + onKeyDown={(e) => { if (e.key === 'Enter' && newModelInput.trim()) { handlePullModel(newModelInput.trim()); setNewModelInput(''); } }} + placeholder="model:tag" + className="flex-1 bg-bg-primary/60 border border-glass-border rounded px-3 py-1.5 text-xs text-text-primary placeholder:text-text-disabled focus:outline-none focus:border-accent-primary" + disabled={isPullingModel !== null} + /> + { if (newModelInput.trim()) { handlePullModel(newModelInput.trim()); setNewModelInput(''); } }} + disabled={isPullingModel !== null || !newModelInput.trim()} + > + Download + +
+
{/* Voice & Embedding Models */} diff --git a/sidecar_server.py b/sidecar_server.py index a60a594..f2f1fe1 100644 --- a/sidecar_server.py +++ b/sidecar_server.py @@ -236,6 +236,9 @@ def _sidecar_tool_event_callback(event_data: dict): "delete-user-account": ["user_id"], "migrate-user-data": ["old_data_dir", "user_id"], "check-first-run": [], + # Local TTS model management + "check-local-tts-models": ["user_id"], + "update-local-tts-models": ["user_id"], } @@ -1413,3 +1416,59 @@ async def _handle_stop_wsl2_tts_managed(self, bridge, params, secrets, request_i return stop_server() except ImportError: return {"error": "WSL2 TTS not available"} + + async def _handle_check_local_tts_models(self, bridge, params, secrets, request_id): + """Check which local TTS/embedding models are installed.""" + import os + from pathlib import Path + hf_home = os.environ.get("HF_HOME", "") + models_dir = os.environ.get("QUBES_MODELS_DIR", "") + kokoro_installed = False + st_installed = False + whisper_installed = False + if hf_home: + hf_path = Path(hf_home) + kokoro_path = hf_path / "hub" / "models--hexgrad--Kokoro-82M" + st_path = hf_path / "hub" / "models--sentence-transformers--all-MiniLM-L6-v2" + kokoro_installed = kokoro_path.exists() + st_installed = st_path.exists() + if models_dir: + whisper_path = Path(models_dir) / "whisper" + whisper_installed = any(whisper_path.glob("*.bin")) if whisper_path.exists() else False + return { + "kokoro_installed": kokoro_installed, + "sentence_transformers_installed": st_installed, + "whisper_installed": whisper_installed, + "hf_home": hf_home, + "models_dir": str(models_dir or hf_home), + } + + async def _handle_update_local_tts_models(self, bridge, params, secrets, request_id): + """Re-download/update local TTS and embedding models from HuggingFace.""" + import os + from pathlib import Path + updated = [] + errors = [] + try: + from huggingface_hub import snapshot_download + hf_home = os.environ.get("HF_HOME", None) + dl_kwargs = {} + if hf_home: + dl_kwargs["cache_dir"] = str(Path(hf_home) / "hub") + try: + snapshot_download("hexgrad/Kokoro-82M", local_files_only=False, **dl_kwargs) + updated.append("kokoro-82m") + except Exception as e: + errors.append(f"Kokoro: {str(e)}") + try: + snapshot_download("sentence-transformers/all-MiniLM-L6-v2", local_files_only=False, **dl_kwargs) + updated.append("sentence-transformers") + except Exception as e: + errors.append(f"sentence-transformers: {str(e)}") + except ImportError as e: + errors.append(f"huggingface_hub not available: {str(e)}") + return { + "success": len(errors) == 0, + "updated": updated, + "errors": errors, + } From 4773ed40ec056e9bb7865210317fafcd10c4be18 Mon Sep 17 00:00:00 2001 From: ABLA Date: Mon, 16 Mar 2026 21:47:10 +0300 Subject: [PATCH 07/24] v0.9.1: enforce wallet 2FA for all backup and restore operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration period ended — wallet authentication is now required for: - Backup (export_account_backup): added wallet_sig param to Rust command and UI; backup button disabled until wallet signed - Restore local file: Restore button disabled until wallet signed - Restore from IPFS: Restore button disabled until wallet signed - LoginScreen: both restore flows require wallet sig before proceeding Login, auto-anchor, and session operations remain password-only (unchanged). Labels updated: "Optional: Wallet 2FA" → "Required: Wallet 2FA" everywhere. Co-Authored-By: Claude Sonnet 4.6 --- qubes-gui/src-tauri/src/lib.rs | 6 +- qubes-gui/src/components/auth/LoginScreen.tsx | 6 +- .../src/components/tabs/QubeManagerTab.tsx | 67 ++++++++++++++++--- 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/qubes-gui/src-tauri/src/lib.rs b/qubes-gui/src-tauri/src/lib.rs index 70d147a..506f7c9 100644 --- a/qubes-gui/src-tauri/src/lib.rs +++ b/qubes-gui/src-tauri/src/lib.rs @@ -6194,7 +6194,8 @@ async fn export_account_backup(app_handle: AppHandle, user_id: String, export_path: String, export_password: String, - master_password: String + master_password: String, + wallet_sig: Option, ) -> Result { validate_identifier(&user_id, "user_id")?; @@ -6205,6 +6206,9 @@ async fn export_account_backup(app_handle: AppHandle, secrets.insert("password", master_password.as_str()); secrets.insert("master_password", master_password.as_str()); secrets.insert("export_password", export_password.as_str()); + if let Some(sig) = &wallet_sig { + secrets.insert("wallet_sig", sig.as_str()); + } let result = sidecar_execute_with_retry("export-account-backup", args, secrets, Some(&app_handle), Some(300)).await?; diff --git a/qubes-gui/src/components/auth/LoginScreen.tsx b/qubes-gui/src/components/auth/LoginScreen.tsx index f867066..b3b6ca1 100644 --- a/qubes-gui/src/components/auth/LoginScreen.tsx +++ b/qubes-gui/src/components/auth/LoginScreen.tsx @@ -259,7 +259,7 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou <>

Connect your BCH wallet (Cashonize / Zapit) to authenticate.{' '} - Required for wallet-bound backups. + Required for all backups and restores.

)} {wcError &&

{wcError}

} diff --git a/qubes-gui/src/components/tabs/QubeManagerTab.tsx b/qubes-gui/src/components/tabs/QubeManagerTab.tsx index ca8d823..3b901ea 100644 --- a/qubes-gui/src/components/tabs/QubeManagerTab.tsx +++ b/qubes-gui/src/components/tabs/QubeManagerTab.tsx @@ -20,6 +20,7 @@ import WalletConnectButton from '../WalletConnectButton'; import { useModels } from '../../hooks/useModels'; import { useChainState } from '../../contexts/ChainStateContext'; import { useVoiceLibrary } from '../../contexts/VoiceLibraryContext'; +import { QRCodeCanvas } from 'qrcode.react'; import { DndContext, closestCenter, @@ -132,6 +133,8 @@ export const QubeManagerTab: React.FC = ({ const [restoreWalletSig, setRestoreWalletSig] = useState(''); const [restoreWcAddress, setRestoreWcAddress] = useState(''); const [isSigningRestoreWc, setIsSigningRestoreWc] = useState(false); + const [restoreWcUri, setRestoreWcUri] = useState(''); + const [restoreWcCopied, setRestoreWcCopied] = useState(false); // Pinata configuration check const [pinataConfigured, setPinataConfigured] = useState(null); @@ -440,6 +443,7 @@ export const QubeManagerTab: React.FC = ({ setIpfsListError(null); setRestoreWalletSig(''); setRestoreWcAddress(''); + setRestoreWcUri(''); }; // Restore (merge) qubes from a backup file @@ -536,13 +540,18 @@ export const QubeManagerTab: React.FC = ({ const handleSignRestoreWc = async () => { setIsSigningRestoreWc(true); + setRestoreWcUri(''); try { - const { getSession, signMessage: wcSignMessage } = await import('../../services/walletConnect'); - const session = await getSession(); + const { connect: wcConnect, getSession, signMessage: wcSignMessage } = await import('../../services/walletConnect'); + let session = await getSession(); + if (!session) { + session = await wcConnect((uri) => setRestoreWcUri(uri)); + setRestoreWcUri(''); + } if (session) { const sig = await wcSignMessage('qubes-backup-key:v1', 'Qubes needs to verify wallet ownership to decrypt your backup', session.topic); setRestoreWalletSig(sig); - setRestoreWcAddress(wallet.address || ''); + setRestoreWcAddress(session.address || wallet.address || ''); } } catch (err) { console.error('WC sign failed:', err); @@ -1516,12 +1525,22 @@ export const QubeManagerTab: React.FC = ({ {!restoreWalletSig ? ( <>

Connect your wallet to prove ownership. Required for all restores.

- {wallet.connected ? ( - - {isSigningRestoreWc ? 'Signing...' : '🔗 Sign with Connected Wallet'} - - ) : ( -

Connect your wallet first using the WalletConnect button.

+ + {isSigningRestoreWc ? 'Connecting...' : '🔗 Connect & Sign Wallet'} + + {restoreWcUri && ( +
+

Scan with Cashonize or Zapit:

+
+ +
+ +
)} ) : ( @@ -1658,12 +1677,22 @@ export const QubeManagerTab: React.FC = ({ {!restoreWalletSig ? ( <>

Connect your wallet to authenticate backup access. Required for all restores.

- {wallet.connected ? ( - - {isSigningRestoreWc ? 'Signing...' : '🔗 Sign with Connected Wallet'} - - ) : ( -

Connect your wallet first using the WalletConnect button.

+ + {isSigningRestoreWc ? 'Connecting...' : '🔗 Connect & Sign Wallet'} + + {restoreWcUri && ( +
+

Scan with Cashonize or Zapit:

+
+ +
+ +
)} ) : ( From 18c16f2947566033e57976a2df6d3d3ade6beb85 Mon Sep 17 00:00:00 2001 From: ABLA Date: Mon, 16 Mar 2026 23:02:05 +0300 Subject: [PATCH 12/24] feat: auto-sync to IPFS after wallet import (received Qube anchors to new owner) After a Qube is received via wallet transfer and imported, automatically trigger IPFS backup so it gets anchored to the recipient's own Pinata account. Co-Authored-By: Claude Sonnet 4.6 --- qubes-gui/src/components/tabs/QubeManagerTab.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qubes-gui/src/components/tabs/QubeManagerTab.tsx b/qubes-gui/src/components/tabs/QubeManagerTab.tsx index 3b901ea..d1c8180 100644 --- a/qubes-gui/src/components/tabs/QubeManagerTab.tsx +++ b/qubes-gui/src/components/tabs/QubeManagerTab.tsx @@ -830,6 +830,10 @@ export const QubeManagerTab: React.FC = ({ if (imported > 0) { setShowImportFromWalletModal(false); resetImportState(); + // Auto-sync to recipient's own IPFS after import so Qube is anchored to their account + if (pinataConfigured) { + handleIpfsBackupAll(true); + } } } catch (error) { alert(`Import failed: ${error}`); From e4420cede15b665e5e64042f8377d9e84590cb6c Mon Sep 17 00:00:00 2001 From: ABLA Date: Mon, 16 Mar 2026 23:33:45 +0300 Subject: [PATCH 13/24] feat: step-by-step progress bar for wallet import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Import from wallet now shows 3 clear steps with animated progress bar: 1. Downloading & decrypting from IPFS (per-Qube progress) 2. Uploading to recipient's own Pinata backup (inline, not background) 3. Done — modal auto-closes after 1.2s IPFS upload is awaited (not silent) so user sees full progress. Modal stays open until everything is safely backed up. Co-Authored-By: Claude Sonnet 4.6 --- .../src/components/tabs/QubeManagerTab.tsx | 70 +++++++++++++++---- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/qubes-gui/src/components/tabs/QubeManagerTab.tsx b/qubes-gui/src/components/tabs/QubeManagerTab.tsx index d1c8180..e07dbf4 100644 --- a/qubes-gui/src/components/tabs/QubeManagerTab.tsx +++ b/qubes-gui/src/components/tabs/QubeManagerTab.tsx @@ -98,7 +98,10 @@ export const QubeManagerTab: React.FC = ({ // Import from wallet modal state (WalletConnect-based, no WIF) const [isScanning, setIsScanning] = useState(false); - const [importProgress, setImportProgress] = useState<{ current: number; total: number; name: string } | null>(null); + const [importProgress, setImportProgress] = useState<{ + current: number; total: number; name: string; + step: 'downloading' | 'uploading' | 'done'; + } | null>(null); // Account backup state const [showBackupModal, setShowBackupModal] = useState(false); @@ -792,9 +795,10 @@ export const QubeManagerTab: React.FC = ({ const errors: string[] = []; try { + // Step 1: Download + decrypt + save each Qube for (let i = 0; i < importable.length; i++) { const q = importable[i]; - setImportProgress({ current: i + 1, total: importable.length, name: q.qube_name }); + setImportProgress({ current: i + 1, total: importable.length, name: q.qube_name, step: 'downloading' }); try { const result = await invoke<{ @@ -805,7 +809,7 @@ export const QubeManagerTab: React.FC = ({ error?: string; }>('import_from_wallet', { userId, - walletWif: '', // Empty = password-based decryption + walletWif: '', categoryId: q.category_id, password: masterPassword, }); @@ -823,17 +827,30 @@ export const QubeManagerTab: React.FC = ({ } } - const summary = [`Imported ${imported} of ${importable.length} Qube(s).`]; - if (failed > 0) summary.push(`\n${failed} failed:\n${errors.join('\n')}`); - alert(summary.join('')); + // Step 2: Upload to recipient's own Pinata + if (imported > 0 && pinataConfigured) { + setImportProgress({ current: importable.length, total: importable.length, name: 'All Qubes', step: 'uploading' }); + try { + await invoke('export_account_backup_ipfs', { + userId, + exportPassword: masterPassword, + masterPassword, + walletSig: null, + }); + } catch (e) { + console.warn('IPFS sync after import failed (non-fatal):', e); + } + } if (imported > 0) { + setImportProgress({ current: importable.length, total: importable.length, name: 'All Qubes', step: 'done' }); + await new Promise(r => setTimeout(r, 1200)); setShowImportFromWalletModal(false); resetImportState(); - // Auto-sync to recipient's own IPFS after import so Qube is anchored to their account - if (pinataConfigured) { - handleIpfsBackupAll(true); - } + } else { + const summary = [`Imported ${imported} of ${importable.length} Qube(s).`]; + if (failed > 0) summary.push(`\n${failed} failed:\n${errors.join('\n')}`); + alert(summary.join('')); } } catch (error) { alert(`Import failed: ${error}`); @@ -1337,10 +1354,35 @@ export const QubeManagerTab: React.FC = ({ {/* Import progress */} {importProgress && ( -
-

- Importing {importProgress.current} of {importProgress.total}: {importProgress.name}... -

+
+ {/* Progress bar */} +
+
+
+ {/* Steps */} +
+ {[ + { key: 'downloading', icon: '⬇️', label: `Downloading & decrypting from IPFS (${importProgress.current}/${importProgress.total})` }, + { key: 'uploading', icon: '☁️', label: 'Uploading to your Pinata backup...' }, + { key: 'done', icon: '✅', label: 'All done — Qube(s) ready' }, + ].map(({ key, icon, label }) => { + const steps = ['downloading', 'uploading', 'done']; + const stepIdx = steps.indexOf(importProgress.step); + const thisIdx = steps.indexOf(key); + const isDone = thisIdx < stepIdx; + const isActive = thisIdx === stepIdx; + return ( +
+ {isDone ? '✓' : isActive ? icon : '○'} + {label} + {isActive && key !== 'done' && ...} +
+ ); + })} +
)} From 6582bf46ba4b736b144a531ce5765d356619eac3 Mon Sep 17 00:00:00 2001 From: ABLA Date: Mon, 16 Mar 2026 23:54:36 +0300 Subject: [PATCH 14/24] feat: per-Qube NFT ownership check at restore time After decrypting each Qube from a backup, verify the restoring wallet currently owns that Qube's NFT on-chain before writing any files. Closes the "old backup" loophole: if Van's NFT was transferred away, an old backup file still containing Van's data will now skip it (skipped_count++) instead of silently restoring it. - gui_bridge.py: import_account_backup + import_account_backup_ipfs accept wallet_address; build NFTVerifier once before loop; skip any Qube whose category_id the wallet no longer holds; unminted Qubes (no nft_metadata) are restored unconditionally - lib.rs: import_account_backup + import_account_backup_ipfs accept wallet_address: Option and forward it via secrets dict - QubeManagerTab.tsx: passes restoreWcAddress to both restore invokes - LoginScreen.tsx: passes wcAddress to both restore invokes Co-Authored-By: Claude Sonnet 4.6 --- gui_bridge.py | 33 +++++++++++++++++-- qubes-gui/src-tauri/src/lib.rs | 6 ++++ qubes-gui/src/components/auth/LoginScreen.tsx | 2 ++ .../src/components/tabs/QubeManagerTab.tsx | 2 ++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/gui_bridge.py b/gui_bridge.py index 35ab1f6..659dde1 100644 --- a/gui_bridge.py +++ b/gui_bridge.py @@ -7512,7 +7512,7 @@ def _read_dir_as_b64(root_dir: Path) -> dict: except Exception: pass - async def import_account_backup_ipfs(self, ipfs_cid: str, import_password: str, master_password: str, wallet_sig: str = "") -> Dict[str, Any]: + async def import_account_backup_ipfs(self, ipfs_cid: str, import_password: str, master_password: str, wallet_sig: str = "", wallet_address: str = "") -> Dict[str, Any]: """ Restore full account backup from IPFS. @@ -7566,7 +7566,7 @@ async def import_account_backup_ipfs(self, ipfs_cid: str, import_password: str, tmp.write(backup_bytes) tmp_path = tmp.name - return await self.import_account_backup(tmp_path, import_password, master_password, wallet_sig=wallet_sig) + return await self.import_account_backup(tmp_path, import_password, master_password, wallet_sig=wallet_sig, wallet_address=wallet_address) except Exception as e: logger.error(f"Failed to import account backup from IPFS: {e}", exc_info=True) @@ -7619,7 +7619,7 @@ async def cleanup_incomplete_qubes(self) -> Dict[str, Any]: logger.error(f"Failed to cleanup incomplete qubes: {e}", exc_info=True) return {"success": False, "error": str(e)} - async def import_account_backup(self, import_path: str, import_password: str, master_password: str, wallet_sig: str = "") -> Dict[str, Any]: + async def import_account_backup(self, import_path: str, import_password: str, master_password: str, wallet_sig: str = "", wallet_address: str = "") -> Dict[str, Any]: """ Import a full account backup from a .qube-backup file. @@ -7734,6 +7734,14 @@ async def import_account_backup(self, import_path: str, import_password: str, ma imported_count = 0 skipped_count = 0 + # Build NFT verifier once if wallet_address provided — used to + # reject Qubes the wallet no longer owns (e.g. transferred away + # before this backup was made). + _nft_verifier = None + if wallet_address: + from blockchain.chain_sync import ChainSyncService + _nft_verifier = ChainSyncService().nft_verifier + for qube_entry in manifest.get("qube_index", []): qube_file = qube_entry["file"] qube_name_entry = qube_entry["name"] @@ -7758,6 +7766,25 @@ async def import_account_backup(self, import_path: str, import_password: str, ma except Exception: return {"success": False, "error": f"Wrong password — could not decrypt Qube {qube_name_entry}."} + # ── NFT Ownership Check ─────────────────────────────────────────────── + # Reject Qubes whose NFT the restoring wallet no longer owns. + # This closes the "old backup" loophole: if you transferred Van's NFT + # away, an old backup file still containing Van should not restore it. + # Qubes without nft_metadata (never minted) are restored unconditionally. + if _nft_verifier: + _nft_meta = qube_export.get("nft_metadata") or {} + _category_id = _nft_meta.get("category_id") + if _category_id: + _owns = await _nft_verifier.verify_ownership(_category_id, wallet_address) + if not _owns: + logger.warning( + f"Skipping Qube '{qube_name_entry}': " + f"wallet {wallet_address[:16]}... does not own NFT {_category_id[:16]}..." + ) + skipped_count += 1 + continue + # ───────────────────────────────────────────────────────────────────── + qube_id = qube_export["qube_id"] qube_name = qube_export["qube_name"] qube_dir_name = f"{qube_name}_{qube_id}" diff --git a/qubes-gui/src-tauri/src/lib.rs b/qubes-gui/src-tauri/src/lib.rs index 506f7c9..b8eaab6 100644 --- a/qubes-gui/src-tauri/src/lib.rs +++ b/qubes-gui/src-tauri/src/lib.rs @@ -6225,6 +6225,7 @@ async fn import_account_backup(app_handle: AppHandle, import_password: String, master_password: String, wallet_sig: Option, + wallet_address: Option, ) -> Result { validate_identifier(&user_id, "user_id")?; @@ -6237,6 +6238,8 @@ async fn import_account_backup(app_handle: AppHandle, secrets.insert("import_password", import_password.as_str()); let wallet_sig_str = wallet_sig.as_deref().unwrap_or(""); secrets.insert("wallet_sig", wallet_sig_str); + let wallet_address_str = wallet_address.as_deref().unwrap_or(""); + secrets.insert("wallet_address", wallet_address_str); let result = sidecar_execute_with_retry("import-account-backup", args, secrets, Some(&app_handle), Some(300)).await?; @@ -6275,6 +6278,7 @@ async fn import_account_backup_ipfs(app_handle: AppHandle, import_password: String, master_password: String, wallet_sig: Option, + wallet_address: Option, ) -> Result { validate_identifier(&user_id, "user_id")?; @@ -6287,6 +6291,8 @@ async fn import_account_backup_ipfs(app_handle: AppHandle, secrets.insert("import_password", import_password.as_str()); let wallet_sig_str = wallet_sig.as_deref().unwrap_or(""); secrets.insert("wallet_sig", wallet_sig_str); + let wallet_address_str = wallet_address.as_deref().unwrap_or(""); + secrets.insert("wallet_address", wallet_address_str); let result = sidecar_execute_with_retry("import-account-backup-ipfs", args, secrets, Some(&app_handle), Some(300)).await?; diff --git a/qubes-gui/src/components/auth/LoginScreen.tsx b/qubes-gui/src/components/auth/LoginScreen.tsx index 92e70a8..c09514d 100644 --- a/qubes-gui/src/components/auth/LoginScreen.tsx +++ b/qubes-gui/src/components/auth/LoginScreen.tsx @@ -129,6 +129,7 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou importPassword: restorePassword, masterPassword: restoreMasterPassword, walletSig: walletSig, + walletAddress: wcAddress || null, }); if (result.success) { @@ -215,6 +216,7 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou importPassword: ipfsRestorePassword, masterPassword: ipfsRestoreMasterPassword, walletSig: walletSig, + walletAddress: wcAddress || null, }); if (result.success) { diff --git a/qubes-gui/src/components/tabs/QubeManagerTab.tsx b/qubes-gui/src/components/tabs/QubeManagerTab.tsx index e07dbf4..a14533a 100644 --- a/qubes-gui/src/components/tabs/QubeManagerTab.tsx +++ b/qubes-gui/src/components/tabs/QubeManagerTab.tsx @@ -462,6 +462,7 @@ export const QubeManagerTab: React.FC = ({ importPassword: restorePassword, masterPassword: masterPassword, walletSig: restoreWalletSig || null, + walletAddress: restoreWcAddress || null, }); if (result.success) { @@ -525,6 +526,7 @@ export const QubeManagerTab: React.FC = ({ importPassword: restorePassword, masterPassword, walletSig: restoreWalletSig || null, + walletAddress: restoreWcAddress || null, } ); if (result.success) { From a6e9c45ebc84d8ad823b4dfcb907597b29855d46 Mon Sep 17 00:00:00 2001 From: ABLA Date: Tue, 17 Mar 2026 00:17:48 +0300 Subject: [PATCH 15/24] feat: scan wallet as third IPFS restore option + rename restore labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - IPFS restore modal now has 3 tabs: Pinata Account | Direct CID | Scan Wallet - Scan Wallet: connects WalletConnect, calls scan_wallet with wallet address, lists found Qubes (with already_imported flag), imports each via import_from_wallet with master password + 3-step progress bar - Both QubeManagerTab (logged-in restore) and LoginScreen (account recovery) - Renamed "Restore from File" → "Restore from Local File" throughout LoginScreen - Copy URL button was already present in walletConnectBlock — rebuild exposes it Co-Authored-By: Claude Sonnet 4.6 --- qubes-gui/src/components/auth/LoginScreen.tsx | 147 ++++++++++++++-- .../src/components/tabs/QubeManagerTab.tsx | 161 +++++++++++++++++- 2 files changed, 291 insertions(+), 17 deletions(-) diff --git a/qubes-gui/src/components/auth/LoginScreen.tsx b/qubes-gui/src/components/auth/LoginScreen.tsx index c09514d..d4d3e59 100644 --- a/qubes-gui/src/components/auth/LoginScreen.tsx +++ b/qubes-gui/src/components/auth/LoginScreen.tsx @@ -30,7 +30,10 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou // Restore from IPFS state const [showIpfsRestoreModal, setShowIpfsRestoreModal] = useState(false); - const [ipfsInputMode, setIpfsInputMode] = useState<'jwt' | 'cid'>('jwt'); + const [ipfsInputMode, setIpfsInputMode] = useState<'jwt' | 'cid' | 'scan'>('jwt'); + const [loginScanQubes, setLoginScanQubes] = useState>([]); + const [isLoginScanning, setIsLoginScanning] = useState(false); + const [loginScanProgress, setLoginScanProgress] = useState<{ current: number; total: number; step: 'downloading' | 'uploading' | 'done' } | null>(null); const [ipfsPinataJwt, setIpfsPinataJwt] = useState(''); const [ipfsDirectCid, setIpfsDirectCid] = useState(''); const [ipfsBackupList, setIpfsBackupList] = useState>([]); @@ -158,6 +161,66 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou } }; + const handleLoginScanWallet = async () => { + if (!wcAddress) return; + setIsLoginScanning(true); + setLoginScanQubes([]); + try { + const result = await invoke<{ success: boolean; qubes?: Array<{ qube_id: string; qube_name: string; category_id: string; ipfs_cid: string; chain_length: number; already_imported?: boolean }>; error?: string }>( + 'scan_wallet', { userId: '_restore', walletAddress: wcAddress } + ); + if (result.success && result.qubes) { + setLoginScanQubes(result.qubes); + if (result.qubes.length === 0) setIpfsListError('No Qubes found for this wallet address.'); + } else { + setIpfsListError(result.error || 'Scan failed'); + } + } catch (err) { + setIpfsListError(`${err}`); + } finally { + setIsLoginScanning(false); + } + }; + + const handleLoginScanRestore = async () => { + const importable = loginScanQubes.filter(q => !q.already_imported); + if (!importable.length || !ipfsRestoreMasterPassword) return; + if (ipfsRestoreMasterPassword !== ipfsRestoreMasterPasswordConfirm) { setIpfsRestoreError('Master passwords do not match.'); return; } + if (ipfsRestoreMasterPassword.length < 8) { setIpfsRestoreError('Master password must be at least 8 characters.'); return; } + setIsIpfsRestoring(true); + setIpfsRestoreError(null); + setLoginScanProgress(null); + let imported = 0; + try { + for (let i = 0; i < importable.length; i++) { + const q = importable[i]; + setLoginScanProgress({ current: i + 1, total: importable.length, step: 'downloading' }); + try { + const result = await invoke<{ success: boolean; qube_id?: string; error?: string }>('import_from_wallet', { + userId: '_restore', walletWif: '', categoryId: q.category_id, password: ipfsRestoreMasterPassword, + }); + if (result.success) imported++; + } catch (err) { + console.error(`Failed to import ${q.qube_name}:`, err); + } + } + if (imported > 0) { + setLoginScanProgress({ current: importable.length, total: importable.length, step: 'done' }); + await new Promise(r => setTimeout(r, 1200)); + alert(`${imported} Qube(s) imported from wallet.\n\nPlease sign in with your master password.`); + setShowIpfsRestoreModal(false); + setUsername('_restore'); + setPassword(ipfsRestoreMasterPassword); + } else { + setIpfsRestoreError('No new Qubes were imported.'); + } + } catch (err) { + setIpfsRestoreError(`${err}`); + } finally { + setIsIpfsRestoring(false); + } + }; + const handleListBackups = async () => { if (!ipfsPinataJwt.trim()) return; setIsListingBackups(true); @@ -378,7 +441,7 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou onClick={() => setShowRestoreModal(true)} className="text-accent-secondary hover:text-accent-secondary/80 text-sm font-medium transition-colors" > - Restore from File + Restore from Local File | +
{/* JWT mode */} @@ -455,6 +524,61 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou
)} + {/* Scan Wallet mode */} + {ipfsInputMode === 'scan' && ( +
+ {!walletSig ? ( +

Connect your wallet below first, then scan.

+ ) : ( + <> + {loginScanQubes.length === 0 && !isLoginScanning && ( + + )} + {isLoginScanning &&

Scanning wallet for Qubes...

} + {loginScanQubes.length > 0 && ( +
+

Found {loginScanQubes.length} Qube(s)

+ {loginScanQubes.map(q => ( +
+ {q.qube_name} + {q.already_imported && (already imported)} +
+ ))} +
+ )} + {loginScanProgress && ( +
+
+
+
+ {[ + { key: 'downloading', icon: '⬇️', label: `Downloading (${loginScanProgress.current}/${loginScanProgress.total})` }, + { key: 'done', icon: '✅', label: 'Done — sign in with your master password' }, + ].map(({ key, icon, label }) => { + const steps = ['downloading', 'done']; + const stepIdx = steps.indexOf(loginScanProgress.step); + const thisIdx = steps.indexOf(key); + return ( +
+ {thisIdx < stepIdx ? '✓' : thisIdx === stepIdx ? icon : '○'} + {label} + {thisIdx === stepIdx && key !== 'done' && ...} +
+ ); + })} +
+ )} + + )} +
+ )} + {ipfsListError && (

{ipfsListError}

@@ -498,8 +622,8 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou {/* WalletConnect — always visible once modal is open */} {walletConnectBlock} - {/* Passwords — shown when CID is known */} - {(ipfsInputMode === 'cid' ? ipfsDirectCid.trim() : ipfsSelectedCid) && ( + {/* Passwords — shown when CID is known or in scan mode */} + {(ipfsInputMode === 'scan' ? loginScanQubes.length > 0 : (ipfsInputMode === 'cid' ? ipfsDirectCid.trim() : ipfsSelectedCid)) && ( <>
@@ -571,14 +695,17 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou > Cancel - {(ipfsInputMode === 'cid' ? ipfsDirectCid.trim() : ipfsSelectedCid) && ( + {(ipfsInputMode === 'scan' ? loginScanQubes.filter(q => !q.already_imported).length > 0 : (ipfsInputMode === 'cid' ? ipfsDirectCid.trim() : ipfsSelectedCid)) && ( - {isIpfsRestoring ? 'Restoring...' : 'Restore Account'} + {isIpfsRestoring ? 'Restoring...' : ipfsInputMode === 'scan' ? 'Import from Wallet' : 'Restore Account'} )}
@@ -586,11 +713,11 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou
)} - {/* Restore from File Modal */} + {/* Restore from Local File Modal */} {showRestoreModal && (
-

Restore from File

+

Restore from Local File

Restore your entire account from a .qube-backup file. This will set up your account and import all Qubes from the backup. diff --git a/qubes-gui/src/components/tabs/QubeManagerTab.tsx b/qubes-gui/src/components/tabs/QubeManagerTab.tsx index a14533a..7fe3f27 100644 --- a/qubes-gui/src/components/tabs/QubeManagerTab.tsx +++ b/qubes-gui/src/components/tabs/QubeManagerTab.tsx @@ -125,7 +125,10 @@ export const QubeManagerTab: React.FC = ({ // Restore mode const [restoreMode, setRestoreMode] = useState<'local' | 'ipfs'>('local'); // IPFS restore state - const [ipfsInputMode, setIpfsInputMode] = useState<'jwt' | 'cid'>('jwt'); + const [ipfsInputMode, setIpfsInputMode] = useState<'jwt' | 'cid' | 'scan'>('jwt'); + const [restoreScanQubes, setRestoreScanQubes] = useState>([]); + const [isRestoreScanning, setIsRestoreScanning] = useState(false); + const [restoreScanProgress, setRestoreScanProgress] = useState<{ current: number; total: number; step: 'downloading' | 'uploading' | 'done' } | null>(null); const [ipfsPinataJwt, setIpfsPinataJwt] = useState(''); const [ipfsDirectCid, setIpfsDirectCid] = useState(''); const [ipfsBackupList, setIpfsBackupList] = useState>([]); @@ -785,6 +788,76 @@ export const QubeManagerTab: React.FC = ({ } }; + // Scan wallet for Qubes in the restore modal context (uses restoreWcAddress) + const handleRestoreScanWallet = async () => { + if (!restoreWcAddress || !userId) return; + setIsRestoreScanning(true); + setRestoreScanQubes([]); + try { + const result = await invoke<{ success: boolean; qubes?: Array<{ qube_id: string; qube_name: string; category_id: string; ipfs_cid: string; chain_length: number; already_imported?: boolean }>; error?: string }>( + 'scan_wallet', { userId, walletAddress: restoreWcAddress } + ); + if (result.success && result.qubes) { + setRestoreScanQubes(result.qubes); + if (result.qubes.length === 0) alert('No Qubes found for this wallet address.'); + } else { + alert(`Scan failed: ${result.error}`); + } + } catch (err) { + alert(`Scan failed: ${err}`); + } finally { + setIsRestoreScanning(false); + } + }; + + // Restore Qubes found by wallet scan (import_from_wallet + auto-anchor) + const handleScanModeRestore = async () => { + const importable = restoreScanQubes.filter(q => !q.already_imported); + if (!importable.length || !masterPassword) return; + setIsRestoring(true); + setRestoreScanProgress(null); + let imported = 0; + try { + for (let i = 0; i < importable.length; i++) { + const q = importable[i]; + setRestoreScanProgress({ current: i + 1, total: importable.length, step: 'downloading' }); + try { + const result = await invoke<{ success: boolean; qube_id?: string; error?: string }>('import_from_wallet', { + userId, walletWif: '', categoryId: q.category_id, password: masterPassword, + }); + if (result.success) { + imported++; + window.dispatchEvent(new CustomEvent('qube-created', { detail: { qube_id: result.qube_id } })); + } + } catch (err) { + console.error(`Failed to import ${q.qube_name}:`, err); + } + } + if (imported > 0 && pinataConfigured) { + setRestoreScanProgress({ current: importable.length, total: importable.length, step: 'uploading' }); + try { + await invoke('export_account_backup_ipfs', { userId, exportPassword: masterPassword, masterPassword, walletSig: null }); + } catch (e) { + console.warn('IPFS sync after scan restore failed (non-fatal):', e); + } + } + if (imported > 0) { + setRestoreScanProgress({ current: importable.length, total: importable.length, step: 'done' }); + await new Promise(r => setTimeout(r, 1200)); + setShowRestoreModal(false); + resetRestoreState(); + setRestoreScanQubes([]); + setRestoreScanProgress(null); + } else { + alert('No new Qubes were imported.'); + } + } catch (err) { + alert(`Restore failed: ${err}`); + } finally { + setIsRestoring(false); + } + }; + // Bulk import all Qubes from wallet (no WIF — password-based decryption) const handleBulkImport = async () => { const importable = walletQubes.filter(q => !q.already_imported); @@ -1642,6 +1715,16 @@ export const QubeManagerTab: React.FC = ({ > Direct CID +

{ipfsInputMode === 'jwt' ? ( @@ -1690,7 +1773,7 @@ export const QubeManagerTab: React.FC = ({
)} - ) : ( + ) : ipfsInputMode === 'cid' ? (
= ({ className="w-full px-3 py-2 bg-glass-bg border border-glass-border rounded-lg text-text-primary text-sm font-mono" />
+ ) : ( + /* Scan Wallet mode */ +
+ {!restoreWalletSig ? ( +

Connect your wallet using the section below, then scan.

+ ) : ( + <> + {restoreScanQubes.length === 0 && !isRestoreScanning && ( + + 🔍 Scan Wallet for Qubes + + )} + {isRestoreScanning && ( +

Scanning wallet for Qubes...

+ )} + {restoreScanQubes.length > 0 && ( +
+

+ Found {restoreScanQubes.length} Qube(s) + {restoreScanQubes.every(q => q.already_imported) && — all already imported} +

+ {restoreScanQubes.map(q => ( +
+ {q.qube_name} + {q.already_imported && (already imported)} + {q.chain_length || 1} block{(q.chain_length || 1) !== 1 ? 's' : ''} +
+ ))} +
+ )} + {restoreScanProgress && ( +
+
+
+
+ {[ + { key: 'downloading', icon: '⬇️', label: `Downloading & decrypting (${restoreScanProgress.current}/${restoreScanProgress.total})` }, + { key: 'uploading', icon: '☁️', label: 'Uploading to your Pinata backup...' }, + { key: 'done', icon: '✅', label: 'All done' }, + ].map(({ key, icon, label }) => { + const steps = ['downloading', 'uploading', 'done']; + const stepIdx = steps.indexOf(restoreScanProgress.step); + const thisIdx = steps.indexOf(key); + return ( +
+ {thisIdx < stepIdx ? '✓' : thisIdx === stepIdx ? icon : '○'} + {label} + {thisIdx === stepIdx && key !== 'done' && ...} +
+ ); + })} +
+ )} + + )} +
)} -
+ {ipfsInputMode !== 'scan' &&
= ({ placeholder="Master password used to encrypt the backup" className="w-full px-3 py-2 bg-glass-bg border border-glass-border rounded-lg text-text-primary text-sm" /> -
+
} {/* WC 2FA */}
@@ -1760,11 +1902,16 @@ export const QubeManagerTab: React.FC = ({ !q.already_imported).length === 0 + : !restorePassword || (ipfsInputMode === 'jwt' ? !ipfsSelectedCid : !ipfsDirectCid.trim())) + } loading={isRestoring} > - {isRestoring ? 'Restoring...' : 'Restore from IPFS'} + {isRestoring ? 'Restoring...' : ipfsInputMode === 'scan' ? 'Import from Wallet' : 'Restore from IPFS'}
From 29fe675cf663bededc3c6e3ef5a596384acca725 Mon Sep 17 00:00:00 2001 From: ABLA Date: Tue, 17 Mar 2026 00:25:54 +0300 Subject: [PATCH 16/24] fix: match login WalletConnect UI to in-app style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Connect Wallet button now uses GlassButton (same as restore modals inside app) - Copy URL button now shows '✓ Copied!' feedback for 2s after click - Adds wcCopied state to walletConnectBlock Co-Authored-By: Claude Sonnet 4.6 --- qubes-gui/src/components/auth/LoginScreen.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/qubes-gui/src/components/auth/LoginScreen.tsx b/qubes-gui/src/components/auth/LoginScreen.tsx index d4d3e59..d32e03b 100644 --- a/qubes-gui/src/components/auth/LoginScreen.tsx +++ b/qubes-gui/src/components/auth/LoginScreen.tsx @@ -52,6 +52,7 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou const [walletSig, setWalletSig] = useState(''); const [isConnectingWallet, setIsConnectingWallet] = useState(false); const [wcError, setWcError] = useState(null); + const [wcCopied, setWcCopied] = useState(false); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!username.trim() || !password.trim()) return; @@ -325,13 +326,15 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou Connect your BCH wallet (Cashonize / Zapit) to authenticate.{' '} Required for all restores.

- + {wcUri && (

Scan with Cashonize or Zapit:

@@ -339,10 +342,10 @@ export const LoginScreen: React.FC = ({ onLogin, onCreateAccou
)} From 6755ec477349e5828f7e4e00ce2321e91b76e5b7 Mon Sep 17 00:00:00 2001 From: ABLA Date: Tue, 17 Mar 2026 00:52:00 +0300 Subject: [PATCH 17/24] feat: fix IPFS sync on auto-anchor + cleaner settings UI with timing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gui_bridge.py: background anchor subprocess now checks auto_sync_ipfs_on_anchor preference and calls sync_qube_to_ipfs_backup after each successful anchor (pin new CID, unpin old — no Pinata bloat) - config/user_preferences.py: default both IPFS sync options to True, 15 min interval - SettingsTab.tsx: restructure into two clear sections (BCH Anchor + IPFS Backup) with timing shown on every row; threshold shows estimated message count; interval dropdown always visible (grayed when periodic disabled); 5 min option added Co-Authored-By: Claude Sonnet 4.6 --- config/user_preferences.py | 6 +- gui_bridge.py | 19 ++ qubes-gui/src/components/tabs/SettingsTab.tsx | 192 +++++++++--------- 3 files changed, 118 insertions(+), 99 deletions(-) diff --git a/config/user_preferences.py b/config/user_preferences.py index 8229c79..a484803 100644 --- a/config/user_preferences.py +++ b/config/user_preferences.py @@ -29,9 +29,9 @@ class BlockPreferences: group_anchor_threshold: int = 20 # Anchor threshold for group chats # IPFS backup settings - auto_sync_ipfs_on_anchor: bool = False # Auto-sync to IPFS after auto-anchor - auto_sync_ipfs_periodic: bool = False # Periodic background sync to IPFS - auto_sync_ipfs_interval: int = 15 # Interval in minutes (15, 30, 45, 60) + auto_sync_ipfs_on_anchor: bool = True # Auto-sync to IPFS after auto-anchor + auto_sync_ipfs_periodic: bool = True # Periodic background sync to IPFS + auto_sync_ipfs_interval: int = 15 # Interval in minutes (5, 15, 30, 60) @dataclass diff --git a/gui_bridge.py b/gui_bridge.py index 659dde1..f5f5449 100644 --- a/gui_bridge.py +++ b/gui_bridge.py @@ -12132,6 +12132,25 @@ def debug_log(msg): lock_file.unlink() debug_log(f"Lock file removed") + # IPFS sync after anchor — honour the auto_sync_ipfs_on_anchor setting + if blocks_anchored > 0: + try: + prefs = user_bridge.orchestrator.get_block_preferences() + if prefs.auto_sync_ipfs_on_anchor: + debug_log(f"auto_sync_ipfs_on_anchor enabled — syncing Qube to IPFS...") + sync_result = await user_bridge.sync_qube_to_ipfs_backup( + qube_id, password, password + ) + if sync_result.get("success"): + debug_log(f"IPFS sync OK, CID={sync_result.get('ipfs_cid')}") + logger.info("auto_anchor_ipfs_sync_completed", qube_id=qube_id, cid=sync_result.get("ipfs_cid")) + else: + debug_log(f"IPFS sync failed (non-fatal): {sync_result.get('error')}") + logger.warning("auto_anchor_ipfs_sync_failed", qube_id=qube_id, error=sync_result.get("error")) + except Exception as ipfs_err: + debug_log(f"IPFS sync error (non-fatal): {ipfs_err}") + logger.warning("auto_anchor_ipfs_sync_error", qube_id=qube_id, error=str(ipfs_err)) + except Exception as e: import traceback tb = traceback.format_exc() diff --git a/qubes-gui/src/components/tabs/SettingsTab.tsx b/qubes-gui/src/components/tabs/SettingsTab.tsx index 5b880ca..afe8e06 100644 --- a/qubes-gui/src/components/tabs/SettingsTab.tsx +++ b/qubes-gui/src/components/tabs/SettingsTab.tsx @@ -126,8 +126,8 @@ export const SettingsTab: React.FC = () => { individual_anchor_threshold: 10, group_auto_anchor: true, group_anchor_threshold: 5, - auto_sync_ipfs_on_anchor: false, - auto_sync_ipfs_periodic: false, + auto_sync_ipfs_on_anchor: true, + auto_sync_ipfs_periodic: true, auto_sync_ipfs_interval: 15, }); const [loadingPreferences, setLoadingPreferences] = useState(true); @@ -1295,7 +1295,7 @@ export const SettingsTab: React.FC = () => { className="w-full flex items-center justify-between text-left" >

- ⚓ Auto-Anchor + ⚓ Anchor & Backup

▼ @@ -1307,15 +1307,17 @@ export const SettingsTab: React.FC = () => { {loadingPreferences ? (

Loading...

) : ( -
- {/* Individual Chat Settings */} -
-

- Individual Chat -

+
+ + {/* BCH Anchor */} +
+

⛓️ BCH Anchor (local + on-chain hash)

+ + {/* Individual Chat */}
+

Individual Chat

- + {blockPreferences.individual_auto_anchor && ( + + )}
-
- {/* Group Chat Settings */} -
-

- Group Chat -

-
+ {/* Group Chat */} +
+

Group Chat

- + {blockPreferences.group_auto_anchor && ( + + )}
- {/* IPFS Sync Settings */} -
-

- IPFS Sync -

-
+ {/* IPFS Backup */} +
+

☁️ IPFS Backup (Pinata)

+ + {/* After anchor */} +

- Automatically upload .qube package to IPFS after each auto-anchor + Upload to Pinata immediately after every auto-anchor (new CID replaces old)

+
-
+
)} From 8544670d0dbca78497f3c927801ed2d69a4264be Mon Sep 17 00:00:00 2001 From: ABLA Date: Tue, 17 Mar 2026 00:54:36 +0300 Subject: [PATCH 18/24] fix: rename BCH Anchor label to Auto-Anchor in settings Co-Authored-By: Claude Sonnet 4.6 --- qubes-gui/src/components/tabs/SettingsTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qubes-gui/src/components/tabs/SettingsTab.tsx b/qubes-gui/src/components/tabs/SettingsTab.tsx index afe8e06..9f5e296 100644 --- a/qubes-gui/src/components/tabs/SettingsTab.tsx +++ b/qubes-gui/src/components/tabs/SettingsTab.tsx @@ -1311,7 +1311,7 @@ export const SettingsTab: React.FC = () => { {/* BCH Anchor */}
-

⛓️ BCH Anchor (local + on-chain hash)

+

⚓ Auto-Anchor

{/* Individual Chat */}
From dbc521f3f1e99e47ad70e9e481cffee2d673a28d Mon Sep 17 00:00:00 2001 From: ABLA Date: Tue, 17 Mar 2026 02:03:29 +0300 Subject: [PATCH 19/24] feat: P2P relay + Nostr integration + Endpoints settings (all 6 phases) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1 (fully implemented): - network/relay_list.py — built-in BitFaced seed relays (Electron Cash pattern) Pre-loaded with well-known BCH Fulcrum nodes and Nostr relays - network/relay_node.py — RelayNodeManager (wraps libp2p p2pd, store-forward) - network/store_forward.py — JSON-backed offline message queue (7-day retention) - network/bundle_manager.py — relay bundle path management (all in Qubes dir) - 11 new Rust commands in lib.rs (relay + endpoints) - 12 new Python commands in gui_bridge.py (relay + endpoints) - RelayPreferences + EndpointPreferences in config/user_preferences.py Phases 2-6 (scaffolded): - network/onion.py — 2-hop onion router stub (Phase 2) - network/cover_traffic.py — uniform-size cover traffic stub (Phase 2) - network/nostr_transport.py — NIP-44 + ephemeral keys stub (Phase 3, BitChat-style) - Phase 4 (mobile), Phase 5 (bundle update), Phase 6 (BLE/LoRa): TODOs in place Settings UI (SettingsTab.tsx): - 📡 Relay Node accordion — local relay start/stop with live green/red status dot (Gupaxx-style), p2pd binary path selector, remote relay status dots (seed + custom), relay bundle update button - 🔌 Endpoints accordion — green terminal-style UI matching reference screenshot: Fulcrum/Electrum nodes, Nostr relays (textareas, one URL per line), Cauldron indexer, Thorchain Midgard, Meta/Icon service, RESET/CANCEL/SAVE buttons, pre-loaded with well-known defaults SDK (@qubesai/sdk): - sdk/src/relay/ — createRelayNode, sendMessage, onMessage, types (Phase 1) - sdk/src/nostr/ — deriveSessionKeypair, nostrSend, nostrListen stubs (Phase 3) - sdk/package.json — ./relay and ./nostr export entries added (10 modules total) README: updated architecture diagram with relay stack, P2P Relay section, SDK module count 8 → 10, relay daemon dev commands relay/: standalone relay daemon for community operators (npm run relay:start / relay:dev / relay:test) Co-Authored-By: Claude Sonnet 4.6 --- README.md | 48 ++- config/user_preferences.py | 111 ++++++ gui_bridge.py | 299 ++++++++++++++++ network/bundle_manager.py | 132 +++++++ network/cover_traffic.py | 75 ++++ network/nostr_transport.py | 130 +++++++ network/onion.py | 80 +++++ network/relay_list.py | 83 +++++ network/relay_node.py | 281 +++++++++++++++ network/store_forward.py | 127 +++++++ qubes-gui/src-tauri/src/lib.rs | 162 ++++++++- qubes-gui/src/components/tabs/SettingsTab.tsx | 336 ++++++++++++++++++ relay/package.json | 16 + relay/relay_config.py | 54 +++ relay/relay_daemon.py | 120 +++++++ sdk/package.json | 10 + sdk/src/nostr/index.ts | 53 +++ sdk/src/nostr/types.ts | 21 ++ sdk/src/relay/index.ts | 50 +++ sdk/src/relay/receive.ts | 49 +++ sdk/src/relay/relay-node.ts | 151 ++++++++ sdk/src/relay/send.ts | 71 ++++ sdk/src/relay/types.ts | 93 +++++ 23 files changed, 2549 insertions(+), 3 deletions(-) create mode 100644 network/bundle_manager.py create mode 100644 network/cover_traffic.py create mode 100644 network/nostr_transport.py create mode 100644 network/onion.py create mode 100644 network/relay_list.py create mode 100644 network/relay_node.py create mode 100644 network/store_forward.py create mode 100644 relay/package.json create mode 100644 relay/relay_config.py create mode 100644 relay/relay_daemon.py create mode 100644 sdk/src/nostr/index.ts create mode 100644 sdk/src/nostr/types.ts create mode 100644 sdk/src/relay/index.ts create mode 100644 sdk/src/relay/receive.ts create mode 100644 sdk/src/relay/relay-node.ts create mode 100644 sdk/src/relay/send.ts create mode 100644 sdk/src/relay/types.ts diff --git a/README.md b/README.md index d3554c1..f0fe459 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,53 @@ Qubes are AI agents you genuinely own. Each Qube has a cryptographic identity mi │ Category: c9054d53dcc075dd7226ea319f20d43d │ │ f102371149311c9239f6c0ea1200b80f │ └─────────────────────────────────────────────┘ + + (parallel stack — no message data ever on-chain) +┌─────────────────────────────────────────────┐ +│ Relay Layer │ +│ Qubes P2P Relay Protocol │ +│ Kademlia DHT · Noise encryption │ +│ 2-hop onion routing · Store-and-forward │ +│ Nostr fallback (Phase 3) │ +└───────────────┬─────────────────────────────┘ + │ transport-agnostic +┌───────────────▼─────────────────────────────┐ +│ Transport Drivers (updatable bundle) │ +│ TCP/WS · QUIC · BLE · LoRa · I2P │ +└─────────────────────────────────────────────┘ +``` + +The desktop app is one implementation of the protocol. The SDK is the protocol itself. The relay layer is a completely separate stack — no message content, metadata, or communication graph ever touches the BCH blockchain. + +--- + +## P2P Relay + +Qubes includes a private peer-to-peer messaging relay for cross-Qube communication. It is transport-agnostic, metadata-private, and requires no central server. + +Every Qubes desktop installation automatically runs a lightweight relay node. Nodes find each other through a Kademlia DHT seeded by a built-in list of community relays — the same pattern Electron Cash uses for Fulcrum servers. Messages route through 2-hop onion encryption (Phase 2) so relay operators cannot determine who is communicating with whom. + +When a recipient is offline, their nearest DHT neighbors hold the encrypted message for up to 7 days. Messages are purged immediately after delivery. Conversation history is stored locally inside the Qube's signed memory chain, not on relays. + +**Transport fallback waterfall** (automatic, no user action required): + +``` +Priority 1 Local relay User's own desktop relay node +Priority 2 Internet DHT Kademlia routing, Noise-encrypted +Priority 3 Nostr fallback Phase 3 — ephemeral keys + NIP-44 encryption +Priority 4 LoRa / BLE mesh Phase 6 — extreme offline, no internet needed +``` + +**Running a dedicated relay node** (for community relay operators): + +```bash +cd relay +npm run relay:start # production, port 4001 +npm run relay:dev # verbose logging +npm run relay:start -- --port 4001 --max-connections 200 --retention-days 7 ``` -The desktop app is one implementation of the protocol. The SDK is the protocol itself. +Every Qubes desktop user already contributes relay capacity just by running the app. Dedicated relay nodes are for always-on community infrastructure, like running a BCH full node. --- @@ -56,7 +100,7 @@ The desktop app is one implementation of the protocol. The SDK is the protocol i npm install @qubesai/sdk ``` -**8 modules:** `types` · `crypto` · `wallet` · `blocks` · `covenant` · `package` · `bcmr` · `storage` +**10 modules:** `types` · `crypto` · `wallet` · `blocks` · `covenant` · `package` · `bcmr` · `storage` · `relay` · `nostr` ### Quick example — generate an identity diff --git a/config/user_preferences.py b/config/user_preferences.py index a484803..aa79df6 100644 --- a/config/user_preferences.py +++ b/config/user_preferences.py @@ -94,6 +94,49 @@ class Qwen3Preferences: ]) +@dataclass +class RelayPreferences: + """Preferences for the P2P relay node.""" + + relay_enabled: bool = True + relay_listen_port: int = 0 # 0 = auto-assign + relay_max_connections: int = 50 + relay_retention_days: int = 7 + relay_custom_peers: List[str] = field(default_factory=list) + p2pd_binary_path: Optional[str] = None # None = use bundled binary + + +@dataclass +class EndpointPreferences: + """Preferences for network endpoint configuration.""" + + fulcrum_nodes: List[str] = field(default_factory=lambda: [ + # Well-known public Fulcrum / Electrum BCH servers (same pre-load as Electron Cash) + "wss://bch.imaginary.cash:50004", + "wss://electroncash.de:50004", + "wss://bch.loping.net:50004", + "wss://blackie.c3-soft.com:50004", + "wss://electroncash.dk:50002", + "wss://electron.jochen-hoenicke.de:51002", + "wss://bitcoincash.network:50004", + "wss://bch.soul-dev.com:50002", + ]) + nostr_relays: List[str] = field(default_factory=lambda: [ + # Well-known public Nostr relays (used as Phase 3 transport fallback) + "wss://relay.damus.io", + "wss://nos.lol", + "wss://relay.nostr.band", + "wss://relay.snort.social", + "wss://nostr.wine", + "wss://relay.current.fyi", + "wss://relay.primal.net", + "wss://nostr-pub.wellorder.net", + ]) + cauldron_indexer: str = "https://indexer.riften.net" + thorchain_midgard: str = "https://midgard.ninerealms.com/v2" + meta_icon_service: str = "https://meta.riften.net" + + @dataclass class OnboardingPreferences: """Track tutorial completion per tab.""" @@ -166,6 +209,8 @@ class UserPreferences: decision: DecisionConfig onboarding: OnboardingPreferences qwen3: Qwen3Preferences + relay: RelayPreferences + endpoints: EndpointPreferences def __init__(self): self.blocks = BlockPreferences() @@ -174,6 +219,8 @@ def __init__(self): self.decision = DecisionConfig() self.onboarding = OnboardingPreferences() self.qwen3 = Qwen3Preferences() + self.relay = RelayPreferences() + self.endpoints = EndpointPreferences() def to_dict(self) -> Dict[str, Any]: """Convert preferences to dictionary.""" @@ -190,6 +237,8 @@ def to_dict(self) -> Dict[str, Any]: 'decision': asdict(self.decision), 'onboarding': asdict(self.onboarding), 'qwen3': qwen3_dict, + 'relay': asdict(self.relay), + 'endpoints': asdict(self.endpoints), } @classmethod @@ -220,6 +269,27 @@ def from_dict(cls, data: Dict[str, Any]) -> 'UserPreferences': voice_library=qwen3_data.get('voice_library', {}), ) + if 'relay' in data: + relay_data = data['relay'] + prefs.relay = RelayPreferences( + relay_enabled=relay_data.get('relay_enabled', True), + relay_listen_port=relay_data.get('relay_listen_port', 0), + relay_max_connections=relay_data.get('relay_max_connections', 50), + relay_retention_days=relay_data.get('relay_retention_days', 7), + relay_custom_peers=relay_data.get('relay_custom_peers', []), + p2pd_binary_path=relay_data.get('p2pd_binary_path', None), + ) + + if 'endpoints' in data: + ep = data['endpoints'] + prefs.endpoints = EndpointPreferences( + fulcrum_nodes=ep.get('fulcrum_nodes', EndpointPreferences().fulcrum_nodes), + nostr_relays=ep.get('nostr_relays', EndpointPreferences().nostr_relays), + cauldron_indexer=ep.get('cauldron_indexer', "https://indexer.riften.net"), + thorchain_midgard=ep.get('thorchain_midgard', "https://midgard.ninerealms.com/v2"), + meta_icon_service=ep.get('meta_icon_service', "https://meta.riften.net"), + ) + return prefs @@ -686,6 +756,47 @@ def update_qwen3_preferences( self.save_preferences(prefs) return prefs + # ========================================================================= + # RELAY PREFERENCES + # ========================================================================= + + def get_relay_preferences(self) -> RelayPreferences: + """Get relay node preferences.""" + return self.load_preferences().relay + + def update_relay_preferences(self, **kwargs) -> UserPreferences: + """Update relay node preferences.""" + prefs = self.load_preferences() + for key, value in kwargs.items(): + if hasattr(prefs.relay, key): + setattr(prefs.relay, key, value) + self.save_preferences(prefs) + return prefs + + # ========================================================================= + # ENDPOINT PREFERENCES + # ========================================================================= + + def get_endpoint_preferences(self) -> EndpointPreferences: + """Get network endpoint preferences.""" + return self.load_preferences().endpoints + + def update_endpoint_preferences(self, **kwargs) -> UserPreferences: + """Update network endpoint preferences.""" + prefs = self.load_preferences() + for key, value in kwargs.items(): + if hasattr(prefs.endpoints, key): + setattr(prefs.endpoints, key, value) + self.save_preferences(prefs) + return prefs + + def reset_endpoint_preferences(self) -> UserPreferences: + """Reset endpoint preferences to defaults.""" + prefs = self.load_preferences() + prefs.endpoints = EndpointPreferences() + self.save_preferences(prefs) + return prefs + def get_voice_clones_dir(self) -> Path: """ Get the directory for storing voice clone audio files. diff --git a/gui_bridge.py b/gui_bridge.py index f5f5449..6d203eb 100644 --- a/gui_bridge.py +++ b/gui_bridge.py @@ -5042,6 +5042,209 @@ async def generate_introduction_message( logger.error(f"Failed to generate introduction: {e}", exc_info=True) return {"success": False, "error": str(e)} + # ========================================================================= + # RELAY NODE COMMANDS — Phase 1 + # ========================================================================= + + async def init_relay_node(self, user_id: str, password: str) -> Dict[str, Any]: + """Start the local P2P relay node.""" + try: + from network.relay_node import RelayNodeManager + from network.bundle_manager import get_p2pd_path + from pathlib import Path + + prefs = self.orchestrator.preferences_manager.get_relay_preferences() + if not prefs.relay_enabled: + return {"success": False, "error": "Relay is disabled in preferences"} + + if not hasattr(self, '_relay_node') or self._relay_node is None: + p2pd = prefs.p2pd_binary_path or str(get_p2pd_path() or "") + self._relay_node = RelayNodeManager( + user_data_dir=self.orchestrator.data_dir, + listen_port=prefs.relay_listen_port, + max_connections=prefs.relay_max_connections, + retention_days=prefs.relay_retention_days, + p2pd_binary=p2pd or None, + custom_peers=prefs.relay_custom_peers, + ) + + await self._relay_node.start() + status = self._relay_node.get_status() + return {"success": True, **status} + except Exception as e: + logger.error(f"init_relay_node failed: {e}", exc_info=True) + return {"success": False, "error": str(e)} + + async def stop_relay_node(self, user_id: str) -> Dict[str, Any]: + """Stop the local P2P relay node.""" + try: + if hasattr(self, '_relay_node') and self._relay_node is not None: + await self._relay_node.stop() + self._relay_node = None + return {"success": True} + except Exception as e: + return {"success": False, "error": str(e)} + + async def get_relay_status(self, user_id: str) -> Dict[str, Any]: + """Get current relay node status.""" + try: + if not hasattr(self, '_relay_node') or self._relay_node is None: + return {"success": True, "running": False, "peer_id": None, "multiaddr": None, "peer_count": 0, "online_peers": 0} + status = self._relay_node.get_status() + return {"success": True, **status} + except Exception as e: + return {"success": False, "error": str(e)} + + async def get_relay_peers(self, user_id: str) -> Dict[str, Any]: + """Get list of relay peers with live reachability status.""" + try: + if not hasattr(self, '_relay_node') or self._relay_node is None: + from network.relay_list import BUILTIN_RELAYS + peers = [] + for r in BUILTIN_RELAYS: + for addr in r["multiaddrs"]: + peers.append({"multiaddr": addr, "label": r["label"], "operator": r["operator"], "builtin": True, "online": None, "latency_ms": None}) + return {"success": True, "peers": peers} + peers = self._relay_node.get_peers() + return {"success": True, "peers": peers} + except Exception as e: + return {"success": False, "error": str(e)} + + async def add_relay_peer(self, user_id: str, multiaddr: str) -> Dict[str, Any]: + """Add a custom relay peer.""" + try: + prefs = self.orchestrator.preferences_manager.get_relay_preferences() + if multiaddr not in prefs.relay_custom_peers: + prefs.relay_custom_peers.append(multiaddr) + self.orchestrator.preferences_manager.update_relay_preferences( + relay_custom_peers=prefs.relay_custom_peers + ) + if hasattr(self, '_relay_node') and self._relay_node: + await self._relay_node.add_peer(multiaddr) + return {"success": True} + except Exception as e: + return {"success": False, "error": str(e)} + + async def remove_relay_peer(self, user_id: str, multiaddr: str) -> Dict[str, Any]: + """Remove a custom relay peer.""" + try: + prefs = self.orchestrator.preferences_manager.get_relay_preferences() + if multiaddr in prefs.relay_custom_peers: + prefs.relay_custom_peers.remove(multiaddr) + self.orchestrator.preferences_manager.update_relay_preferences( + relay_custom_peers=prefs.relay_custom_peers + ) + if hasattr(self, '_relay_node') and self._relay_node: + await self._relay_node.remove_peer(multiaddr) + return {"success": True} + except Exception as e: + return {"success": False, "error": str(e)} + + async def update_relay_preferences(self, user_id: str, password: str, **kwargs) -> Dict[str, Any]: + """Update relay node preferences.""" + try: + prefs = self.orchestrator.preferences_manager.update_relay_preferences(**kwargs) + from dataclasses import asdict + return {"success": True, "relay": asdict(prefs.relay)} + except Exception as e: + return {"success": False, "error": str(e)} + + async def send_direct_p2p_message( + self, + qube_id: str, + recipient_qube_id: str, + recipient_pub_key: str, + message: str, + password: str, + ) -> Dict[str, Any]: + """Send an encrypted direct P2P message to another Qube via relay.""" + try: + from network.messaging import QubeMessage, EncryptedSession + from cryptography.hazmat.primitives.asymmetric import ec + from cryptography.hazmat.backends import default_backend + import json, base64 + + self.orchestrator.set_master_key(password) + qube = self.orchestrator.qubes.get(qube_id) + if not qube: + return {"success": False, "error": f"Qube {qube_id} not loaded"} + + # Encrypt message payload using ECIES-like pattern + payload = json.dumps({"type": "text", "content": message, "sender_qube_id": qube_id}).encode() + + if not hasattr(self, '_relay_node') or self._relay_node is None: + return {"success": False, "error": "Relay node not running. Start relay first."} + + await self._relay_node.send_message( + recipient_qube_id=recipient_qube_id, + encrypted_payload=payload, # TODO Phase 2: wrap with onion + ttl_days=7, + ) + return {"success": True} + except Exception as e: + logger.error(f"send_direct_p2p_message failed: {e}", exc_info=True) + return {"success": False, "error": str(e)} + + async def get_direct_p2p_messages(self, qube_id: str, password: str) -> Dict[str, Any]: + """Drain store-and-forward queue for a Qube.""" + try: + if not hasattr(self, '_relay_node') or self._relay_node is None: + return {"success": True, "messages": []} + messages = await self._relay_node.get_pending_messages(qube_id) + return {"success": True, "messages": messages} + except Exception as e: + return {"success": False, "error": str(e)} + + async def check_relay_bundle_update(self, user_id: str) -> Dict[str, Any]: + """Check if a newer relay bundle is available. Phase 5.""" + try: + from network.bundle_manager import check_for_update + from pathlib import Path + result = await check_for_update(self.orchestrator.data_dir) + return {"success": True, **result} + except Exception as e: + return {"success": False, "error": str(e)} + + async def update_relay_bundle(self, user_id: str) -> Dict[str, Any]: + """Download and apply the latest relay bundle. Phase 5.""" + try: + from network.bundle_manager import update_bundle + result = await update_bundle(self.orchestrator.data_dir) + return {"success": True, **result} + except Exception as e: + return {"success": False, "error": str(e)} + + # ========================================================================= + # ENDPOINT PREFERENCES COMMANDS + # ========================================================================= + + async def get_endpoint_preferences(self, user_id: str) -> Dict[str, Any]: + """Get network endpoint preferences.""" + try: + from dataclasses import asdict + prefs = self.orchestrator.preferences_manager.get_endpoint_preferences() + return {"success": True, "endpoints": asdict(prefs)} + except Exception as e: + return {"success": False, "error": str(e)} + + async def update_endpoint_preferences(self, user_id: str, password: str, **kwargs) -> Dict[str, Any]: + """Update network endpoint preferences.""" + try: + from dataclasses import asdict + prefs = self.orchestrator.preferences_manager.update_endpoint_preferences(**kwargs) + return {"success": True, "endpoints": asdict(prefs.endpoints)} + except Exception as e: + return {"success": False, "error": str(e)} + + async def reset_endpoint_preferences(self, user_id: str, password: str) -> Dict[str, Any]: + """Reset endpoint preferences to defaults.""" + try: + from dataclasses import asdict + prefs = self.orchestrator.preferences_manager.reset_endpoint_preferences() + return {"success": True, "endpoints": asdict(prefs.endpoints)} + except Exception as e: + return {"success": False, "error": str(e)} + async def send_introduction(self, qube_id: str, to_commitment: str, message: str, password: str) -> Dict[str, Any]: """Send an introduction request to another Qube""" try: @@ -14974,6 +15177,102 @@ def debug_log(msg): ) print(json.dumps(result)) + # ===================================================================== + # RELAY NODE COMMANDS — Phase 1 + # ===================================================================== + elif command == "init-relay-node": + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + password = get_secret("password", argv_index=3) + result = await user_bridge.init_relay_node(user_id, password) + print(json.dumps(result)) + + elif command == "stop-relay-node": + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + result = await user_bridge.stop_relay_node(user_id) + print(json.dumps(result)) + + elif command == "get-relay-status": + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + result = await user_bridge.get_relay_status(user_id) + print(json.dumps(result)) + + elif command == "get-relay-peers": + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + result = await user_bridge.get_relay_peers(user_id) + print(json.dumps(result)) + + elif command == "add-relay-peer": + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + multiaddr = sys.argv[3] if len(sys.argv) > 3 else "" + result = await user_bridge.add_relay_peer(user_id, multiaddr) + print(json.dumps(result)) + + elif command == "remove-relay-peer": + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + multiaddr = sys.argv[3] if len(sys.argv) > 3 else "" + result = await user_bridge.remove_relay_peer(user_id, multiaddr) + print(json.dumps(result)) + + elif command == "update-relay-preferences": + import json as _json + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + password = get_secret("password", argv_index=3) + kwargs = _json.loads(sys.argv[4]) if len(sys.argv) > 4 else {} + result = await user_bridge.update_relay_preferences(user_id, password, **kwargs) + print(_json.dumps(result)) + + elif command == "send-direct-p2p-message": + if len(sys.argv) < 6: + print(json.dumps({"error": "user_id, qube_id, recipient_qube_id, recipient_pub_key required"}), file=sys.stderr) + sys.exit(1) + user_id = sys.argv[2] + qube_id = validate_qube_id(sys.argv[3]) + recipient_qube_id = sys.argv[4] + recipient_pub_key = sys.argv[5] + message = sys.argv[6] if len(sys.argv) > 6 else "" + password = get_secret("password", argv_index=7) + result = await user_bridge.send_direct_p2p_message(qube_id, recipient_qube_id, recipient_pub_key, message, password) + print(json.dumps(result)) + + elif command == "get-direct-p2p-messages": + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + qube_id = validate_qube_id(sys.argv[3]) if len(sys.argv) > 3 else "" + password = get_secret("password", argv_index=4) + result = await user_bridge.get_direct_p2p_messages(qube_id, password) + print(json.dumps(result)) + + elif command == "check-relay-bundle-update": + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + result = await user_bridge.check_relay_bundle_update(user_id) + print(json.dumps(result)) + + elif command == "update-relay-bundle": + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + result = await user_bridge.update_relay_bundle(user_id) + print(json.dumps(result)) + + # ===================================================================== + # ENDPOINT PREFERENCES COMMANDS + # ===================================================================== + elif command == "get-endpoint-preferences": + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + result = await user_bridge.get_endpoint_preferences(user_id) + print(json.dumps(result)) + + elif command == "update-endpoint-preferences": + import json as _json + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + password = get_secret("password", argv_index=3) + kwargs = _json.loads(sys.argv[4]) if len(sys.argv) > 4 else {} + result = await user_bridge.update_endpoint_preferences(user_id, password, **kwargs) + print(_json.dumps(result)) + + elif command == "reset-endpoint-preferences": + user_id = sys.argv[2] if len(sys.argv) > 2 else "default_user" + password = get_secret("password", argv_index=3) + result = await user_bridge.reset_endpoint_preferences(user_id, password) + print(json.dumps(result)) + elif command == "send-introduction": if len(sys.argv) < 6: print(json.dumps({"error": "User ID, Qube ID, to_commitment, and message required"}), file=sys.stderr) diff --git a/network/bundle_manager.py b/network/bundle_manager.py new file mode 100644 index 0000000..6541745 --- /dev/null +++ b/network/bundle_manager.py @@ -0,0 +1,132 @@ +""" +Relay Bundle Manager — Phase 5 + +Manages the relay bundle: p2pd binary, relay list, and all networking +libraries as a single independently-updatable package stored inside the +Qubes app data directory. + +Bundle lives at: {qubes_data_dir}/relay_bundle/ + relay_bundle/ + p2pd[.exe] — Go libp2p daemon binary + relay_list.json — Community relay list (updated separately from app) + bundle_version.txt — Current bundle version string + bundle_manifest.json — Version, checksums, update URL + +The Settings → Endpoints → "Update Bundle" button calls update_bundle(). +Signature verification is done against BitFaced's public key hardcoded here. + +TODO Phase 5: Implement download, verify, atomic replace logic. +""" + +import json +import os +import platform +import sys +from pathlib import Path +from typing import Any, Dict, Optional + +from utils.logging import get_logger + +logger = get_logger(__name__) + +# BitFaced's bundle signing public key (secp256k1 compressed hex). +# Hardcoded at compile time — bundles that fail this check are silently rejected. +BUNDLE_SIGNING_PUBKEY = "TODO_REPLACE_WITH_BITFACED_PUBKEY" + +# Canonical bundle manifest URL (mirrored by community volunteers). +BUNDLE_MANIFEST_URL = "https://qube.cash/relay-bundle/manifest.json" + + +def get_bundle_dir(qubes_data_dir: Optional[Path] = None) -> Path: + """ + Return the relay bundle directory inside the Qubes app data path. + + All relay binaries and configs live here — one clear location. + """ + if qubes_data_dir: + bundle_dir = Path(qubes_data_dir) / "relay_bundle" + elif getattr(sys, "frozen", False): + # PyInstaller bundle — place alongside the executable + bundle_dir = Path(sys.executable).parent / "relay_bundle" + else: + # Dev mode — use project root + bundle_dir = Path(__file__).resolve().parent.parent / "relay_bundle" + + bundle_dir.mkdir(parents=True, exist_ok=True) + return bundle_dir + + +def get_p2pd_path(qubes_data_dir: Optional[Path] = None) -> Optional[Path]: + """ + Return path to the bundled p2pd binary, or None if not present. + + Falls back to system PATH if not found in bundle dir. + """ + bundle_dir = get_bundle_dir(qubes_data_dir) + binary_name = "p2pd.exe" if platform.system() == "Windows" else "p2pd" + bundled = bundle_dir / binary_name + + if bundled.exists(): + return bundled + + # Fall back to system PATH + import shutil + system_p2pd = shutil.which("p2pd") + if system_p2pd: + return Path(system_p2pd) + + return None + + +def get_bundle_version(qubes_data_dir: Optional[Path] = None) -> str: + """Return the installed bundle version, or 'none' if not installed.""" + version_file = get_bundle_dir(qubes_data_dir) / "bundle_version.txt" + if version_file.exists(): + return version_file.read_text(encoding="utf-8").strip() + return "none" + + +def get_bundle_manifest(qubes_data_dir: Optional[Path] = None) -> Dict[str, Any]: + """Return the installed bundle manifest.""" + manifest_file = get_bundle_dir(qubes_data_dir) / "bundle_manifest.json" + if manifest_file.exists(): + try: + return json.loads(manifest_file.read_text(encoding="utf-8")) + except json.JSONDecodeError: + pass + return {} + + +# TODO Phase 5: implement check_for_update(), download_bundle(), verify_signature(), +# apply_bundle() — atomic replace + daemon restart without full app restart. +async def check_for_update(qubes_data_dir: Optional[Path] = None) -> Dict[str, Any]: + """ + Check if a newer relay bundle is available. + + TODO Phase 5: fetch BUNDLE_MANIFEST_URL, compare versions, return result. + """ + return { + "current_version": get_bundle_version(qubes_data_dir), + "latest_version": "unknown", + "update_available": False, + "note": "Phase 5 — not yet implemented", + } + + +async def update_bundle(qubes_data_dir: Optional[Path] = None) -> Dict[str, Any]: + """ + Download, verify, and apply the latest relay bundle. + + TODO Phase 5: full implementation. + Steps: + 1. Fetch manifest from BUNDLE_MANIFEST_URL + 2. Compare version with installed + 3. Download bundle archive + 4. Verify secp256k1 signature against BUNDLE_SIGNING_PUBKEY + 5. Atomically replace relay_bundle/ directory + 6. Restart relay daemon without restarting full app + """ + return { + "success": False, + "message": "Relay bundle update not yet implemented (Phase 5).", + } diff --git a/network/cover_traffic.py b/network/cover_traffic.py new file mode 100644 index 0000000..5492604 --- /dev/null +++ b/network/cover_traffic.py @@ -0,0 +1,75 @@ +""" +Cover Traffic Manager — Phase 2 (Scaffold) + +Sends dummy encrypted packets at a constant rate to make real message traffic +indistinguishable from background noise. An observer watching any relay node +sees a constant stream of uniform-size packets regardless of real activity. + +Inspired by GNUnet's CADET anonymity model: + "All peers act as routers and use link-encrypted connections with stable + bandwidth utilization to communicate with each other." + +TODO Phase 2: Implement real cover traffic emission. +""" + +import asyncio +from typing import Optional + +from utils.logging import get_logger + +logger = get_logger(__name__) + +# Default dummy packet size must match OnionRouter.pad_to_bytes +PACKET_SIZE_BYTES = 512 + +# Default cover traffic rate: 1 dummy packet per second per active peer connection. +# Configurable via RelayPreferences in a future update. +DEFAULT_RATE_HZ = 1.0 + + +class CoverTrafficManager: + """ + Emits dummy encrypted packets to camouflage real message timing. + + Phase 2 TODO: + - Select random peers from relay's known_peers list + - Encrypt a random payload with the peer's pubkey (indistinguishable from real traffic) + - Send at DEFAULT_RATE_HZ regardless of real message activity + - Uniform packet size matching OnionRouter.pad_to_bytes + """ + + def __init__(self, rate_hz: float = DEFAULT_RATE_HZ): + self.rate_hz = rate_hz + self._task: Optional[asyncio.Task] = None + self._running = False + + async def start(self, relay_node) -> None: + """ + Start emitting cover traffic. + + Args: + relay_node: RelayNodeManager instance to send dummy packets through. + + TODO Phase 2: implement. + """ + if self._running: + return + self._running = True + self._task = asyncio.create_task(self._emit_loop(relay_node)) + logger.info("cover_traffic_started_stub") + + async def stop(self) -> None: + """Stop cover traffic emission.""" + self._running = False + if self._task: + self._task.cancel() + self._task = None + logger.info("cover_traffic_stopped") + + async def _emit_loop(self, relay_node) -> None: + """Emit dummy packets at the configured rate. TODO Phase 2.""" + interval = 1.0 / self.rate_hz + while self._running: + # TODO Phase 2: pick random peer, send dummy encrypted packet + logger.debug("cover_traffic_emit_todo") + await asyncio.sleep(interval) diff --git a/network/nostr_transport.py b/network/nostr_transport.py new file mode 100644 index 0000000..01f4abd --- /dev/null +++ b/network/nostr_transport.py @@ -0,0 +1,130 @@ +""" +Nostr Transport — Phase 3 (Scaffold) + +Uses public Nostr relays as a Priority-4 transport fallback when the DHT +is unreachable (no internet, ISP blocks libp2p ports, etc.). + +Privacy design (BitChat-style with maximum privacy): + - Ephemeral keypair per session — NOT the user's BCH wallet keypair. + The Nostr identity is mathematically unlinked from the BCH / Qube identity. + - NIP-44 encrypted DMs (ChaCha20-Poly1305 + secp256k1 ECDH key agreement). + - Broadcast to all configured Nostr relays simultaneously. + - Recipient identified by a session-derived ephemeral pubkey known only to + sender (derived via HKDF(qube_privkey, recipient_qube_id, session_id)). + - An observer watching Nostr relays sees encrypted blobs from disposable + ephemeral pubkeys — no link to real BCH/Qube identities. + +Nostr relay list comes from EndpointPreferences.nostr_relays (Settings → Endpoints). + +TODO Phase 3: Implement WebSocket connections, NIP-44 encryption, event publishing. +""" + +import hashlib +import hmac +from typing import Any, Callable, Dict, List, Optional, Tuple + +from utils.logging import get_logger + +logger = get_logger(__name__) + +# NIP-44 version tag +NIP44_VERSION = b"\x02" + + +class NostrTransport: + """ + Nostr relay transport with NIP-44 + ephemeral keypairs. + + Phase 3 TODO: + - Open WebSocket connections to each relay in relay_urls + - Subscribe to ephemeral inbox pubkey (NIP-01 REQ filter) + - Publish NIP-44 encrypted DMs via EVENT messages + - Handle reconnection, relay failures, and message deduplication + """ + + def __init__(self, relay_urls: List[str]): + """ + Args: + relay_urls: List of Nostr relay WebSocket URLs. + Defaults to EndpointPreferences.nostr_relays if empty. + """ + self.relay_urls = relay_urls + self._connections: Dict[str, Any] = {} # url → WebSocket (Phase 3) + self._running = False + + async def start(self) -> None: + """Connect to all configured Nostr relays. TODO Phase 3.""" + logger.info("nostr_transport_start_stub", relay_count=len(self.relay_urls)) + + async def stop(self) -> None: + """Disconnect from all Nostr relays. TODO Phase 3.""" + self._running = False + logger.info("nostr_transport_stopped") + + async def send( + self, + recipient_ephemeral_pubkey: str, + encrypted_payload: bytes, + ) -> bool: + """ + Publish a NIP-44 encrypted DM to all relays. + + Args: + recipient_ephemeral_pubkey: secp256k1 hex pubkey of recipient's ephemeral inbox. + encrypted_payload: Already encrypted bytes (ECIES from network/messaging.py). + + TODO Phase 3: wrap in NIP-44, publish as Nostr EVENT kind 4. + """ + logger.info( + "nostr_send_stub", + recipient_key=recipient_ephemeral_pubkey[:16] + "...", + payload_len=len(encrypted_payload), + ) + return False # Phase 3 TODO + + async def listen( + self, + my_ephemeral_privkey: str, + handler: Callable[[bytes], None], + ) -> None: + """ + Subscribe to incoming messages for my_ephemeral_privkey. + + Args: + my_ephemeral_privkey: secp256k1 hex privkey of our ephemeral inbox. + handler: Callback called with decrypted payload bytes on each message. + + TODO Phase 3: NIP-01 REQ subscription + NIP-44 decryption. + """ + logger.info("nostr_listen_stub") + + @staticmethod + def derive_session_keypair( + qube_privkey_bytes: bytes, + recipient_qube_id: str, + session_id: str, + ) -> Tuple[bytes, bytes]: + """ + Derive an ephemeral (privkey, pubkey) pair for a session. + + Uses HKDF so the Nostr keypair is deterministic per session but + completely unlinkable to the BCH wallet keypair by an outside observer. + + Args: + qube_privkey_bytes: Raw bytes of the Qube's secp256k1 private key. + recipient_qube_id: Recipient's 8-char Qube ID. + session_id: Unique session identifier (e.g., conversation_id). + + Returns: + (ephemeral_privkey_bytes, ephemeral_pubkey_bytes) + + TODO Phase 3: replace stub HKDF with proper secp256k1 key derivation. + """ + # HKDF-SHA256 stub + ikm = qube_privkey_bytes + info = f"qubes-nostr-session:{recipient_qube_id}:{session_id}".encode() + prk = hmac.new(b"qubes-nostr", ikm, hashlib.sha256).digest() + ephemeral_privkey = hmac.new(prk, info + b"\x01", hashlib.sha256).digest() + # TODO Phase 3: derive secp256k1 pubkey from ephemeral_privkey + ephemeral_pubkey = b"\x00" * 33 # placeholder + return ephemeral_privkey, ephemeral_pubkey diff --git a/network/onion.py b/network/onion.py new file mode 100644 index 0000000..1efce18 --- /dev/null +++ b/network/onion.py @@ -0,0 +1,80 @@ +""" +Onion Router — Phase 2 (Scaffold) + +2-hop onion routing to hide the communication graph from relay operators. +When Alice sends to Bob, she picks 2 intermediate relays (R1, R2) and wraps +the payload in 3 encryption layers: + + Outer → encrypted for R1, contains: addr of R2 + middle layer + Middle → encrypted for R2, contains: addr of Bob's relay + inner layer + Inner → encrypted for Bob, contains: actual message payload + +R1 learns only: "forward to R2". R2 learns only: "forward to Bob's relay". +Neither learns Alice is the origin. Bob's relay learns only the final destination. + +TODO Phase 2: Implement wrap_message / unwrap_message using Noise XX handshake. +""" + +from typing import Optional, Tuple +from utils.logging import get_logger + +logger = get_logger(__name__) + + +class OnionRouter: + """ + 2-hop onion routing for Qubes P2P relay. + + Used by RelayNodeManager.send_message() before dispatching to the DHT. + + Phase 2 TODO: + - Select 2 random relay nodes from DHT routing table as R1, R2 + - Wrap payload in 3 Noise-encrypted layers (outer for R1, middle for R2, inner for Bob) + - Pad all packets to uniform size (512 bytes default) + - Implement unwrap_message for relay nodes that need to forward + """ + + def __init__(self, pad_to_bytes: int = 512): + self.pad_to_bytes = pad_to_bytes + + async def wrap_message( + self, + payload: bytes, + r1_pubkey: bytes, + r2_pubkey: bytes, + dest_pubkey: bytes, + dest_relay_addr: str, + r2_relay_addr: str, + ) -> bytes: + """ + Wrap payload in 2-hop onion layers. + + TODO Phase 2: implement Noise XX multi-layer encryption. + """ + logger.debug("onion_wrap_todo", payload_len=len(payload)) + # Stub: return payload unmodified until Phase 2 + return payload + + async def unwrap_message( + self, + onion_packet: bytes, + my_privkey: bytes, + ) -> Tuple[bytes, Optional[str]]: + """ + Peel one onion layer. + + Returns: + (inner_payload, next_hop_addr) — forward inner to next_hop if not None. + If next_hop is None, this node is the final recipient. + + TODO Phase 2: implement Noise XX layer decryption. + """ + logger.debug("onion_unwrap_todo", packet_len=len(onion_packet)) + # Stub: treat packet as plain payload, no next hop + return onion_packet, None + + def _pad(self, data: bytes) -> bytes: + """Pad data to uniform packet size to prevent size-based traffic analysis.""" + if len(data) < self.pad_to_bytes: + return data + b"\x00" * (self.pad_to_bytes - len(data)) + return data diff --git a/network/relay_list.py b/network/relay_list.py new file mode 100644 index 0000000..85336d4 --- /dev/null +++ b/network/relay_list.py @@ -0,0 +1,83 @@ +""" +Built-in Relay List + +Pre-loaded list of seed relays, following the same pattern as Electron Cash's +built-in Fulcrum server list. As the community grows, more relays will be added. + +Users can add/remove any relay via Settings → Relay Node → Custom Relays. +Full user control — even seed relays can be removed. +""" + +from typing import List, Dict, Any + + +# BitFaced-operated seed relays (always present, maintained long-term). +# BitFaced fills in real multiaddrs and peer IDs before deploying. +BUILTIN_RELAYS: List[Dict[str, Any]] = [ + # ------------------------------------------------------------------------- + # BitFaced Seed Relays (official) + # ------------------------------------------------------------------------- + { + "id": "QmRelayUS", + "label": "BitFaced Relay US-East", + "operator": "BitFaced", + "region": "US-East", + "multiaddrs": [ + "/ip4/relay-us.qube.cash/tcp/4001/p2p/QmRelayUS", + "/ip4/relay-us.qube.cash/tcp/443/wss/p2p/QmRelayUS", + ], + }, + { + "id": "QmRelayEU", + "label": "BitFaced Relay EU", + "operator": "BitFaced", + "region": "EU", + "multiaddrs": [ + "/ip4/relay-eu.qube.cash/tcp/4001/p2p/QmRelayEU", + "/ip4/relay-eu.qube.cash/tcp/443/wss/p2p/QmRelayEU", + ], + }, + { + "id": "QmRelayAS", + "label": "BitFaced Relay Asia", + "operator": "BitFaced", + "region": "Asia", + "multiaddrs": [ + "/ip4/relay-as.qube.cash/tcp/4001/p2p/QmRelayAS", + "/ip4/relay-as.qube.cash/tcp/443/wss/p2p/QmRelayAS", + ], + }, + # ------------------------------------------------------------------------- + # Community Relay Slots + # Open an issue with label 'relay-listing' to get your relay added here. + # Requirements: 30+ days uptime, known operator, open to all Qubes users. + # ------------------------------------------------------------------------- + # { + # "id": "QmCommRelay1", + # "label": "Community Relay 1", + # "operator": "TBD", + # "region": "TBD", + # "multiaddrs": [], + # }, +] + + +def get_all_bootstrap_multiaddrs() -> List[str]: + """Return flat list of all multiaddrs from the built-in relay list.""" + addrs = [] + for relay in BUILTIN_RELAYS: + addrs.extend(relay["multiaddrs"]) + return addrs + + +def get_relay_by_id(relay_id: str) -> Dict[str, Any] | None: + """Look up a built-in relay by its ID.""" + for relay in BUILTIN_RELAYS: + if relay["id"] == relay_id: + return relay + return None + + +def get_relay_labels() -> List[str]: + """Return human-readable labels for all built-in relays.""" + return [r["label"] for r in BUILTIN_RELAYS] diff --git a/network/relay_node.py b/network/relay_node.py new file mode 100644 index 0000000..05a0f7a --- /dev/null +++ b/network/relay_node.py @@ -0,0 +1,281 @@ +""" +Relay Node Manager — Phase 1 + +High-level manager for the Qubes P2P relay node. Wraps QubeP2PNode and +LibP2PDaemonBridge, loads the built-in relay list, manages custom peers, +and exposes a clean interface to gui_bridge.py. + +Transport fallback waterfall (Priority 1 → 5): + 1. Direct local relay (this node) + 2. Internet DHT routing (Kademlia via p2pd) + 3. Nostr relay fallback (Phase 3 — see nostr_transport.py) + 4. BLE / LoRa mesh (Phase 6) +""" + +import asyncio +import json +import time +from pathlib import Path +from typing import Any, Dict, List, Optional + +from network.p2p_node import QubeP2PNode +from network.libp2p_daemon_bridge import LibP2PDaemonBridge +from network.relay_list import BUILTIN_RELAYS, get_all_bootstrap_multiaddrs +from network.store_forward import StoreForwardQueue +from utils.logging import get_logger + +logger = get_logger(__name__) + + +class RelayNodeManager: + """ + High-level P2P relay node for Qubes. + + Manages: + - Local p2pd daemon lifecycle (start / stop) + - Built-in + user-configured seed peers + - Outbound message sending via DHT routing + - Inbound store-and-forward message delivery + - Peer reachability polling (for UI status dots) + """ + + def __init__( + self, + user_data_dir: Path, + listen_port: int = 0, + max_connections: int = 50, + retention_days: int = 7, + p2pd_binary: Optional[str] = None, + custom_peers: Optional[List[str]] = None, + ): + self.user_data_dir = Path(user_data_dir) + self.listen_port = listen_port + self.max_connections = max_connections + self.retention_days = retention_days + self.p2pd_binary = p2pd_binary # None = use bundled binary + + # Built-in + user custom peers + bootstrap = get_all_bootstrap_multiaddrs() + if custom_peers: + bootstrap.extend(custom_peers) + self.bootstrap_peers = bootstrap + self.custom_peers: List[str] = list(custom_peers or []) + + # p2pd bridge (initialised in start()) + self._bridge: Optional[LibP2PDaemonBridge] = None + self._p2p_node: Optional[QubeP2PNode] = None + + # Store-and-forward queue + queue_dir = self.user_data_dir / "relay_queue" + queue_dir.mkdir(parents=True, exist_ok=True) + self.store_forward = StoreForwardQueue(queue_dir, retention_days=retention_days) + + # Runtime state + self.is_running = False + self.peer_id: Optional[str] = None + self.multiaddr: Optional[str] = None + + # Peer reachability cache: multiaddr → {online, latency_ms, last_checked} + self._peer_status: Dict[str, Dict[str, Any]] = {} + self._poll_task: Optional[asyncio.Task] = None + + logger.info("relay_node_manager_initialized", listen_port=listen_port) + + # ------------------------------------------------------------------------- + # Lifecycle + # ------------------------------------------------------------------------- + + async def start(self) -> None: + """Start the local relay daemon and connect to bootstrap peers.""" + if self.is_running: + return + + try: + self._bridge = LibP2PDaemonBridge( + qube_id="relay", + listen_port=self.listen_port, + bootstrap_peers=self.bootstrap_peers, + daemon_binary=self.p2pd_binary, + ) + await self._bridge.start() + + self.peer_id = getattr(self._bridge, "peer_id", None) + self.multiaddr = getattr(self._bridge, "multiaddr", None) + self.is_running = True + + # Purge expired messages from the queue on startup + self.store_forward.purge_expired() + + # Start background peer polling (every 30 s) + self._poll_task = asyncio.create_task(self._poll_peers_loop()) + + logger.info("relay_node_started", peer_id=self.peer_id, multiaddr=self.multiaddr) + + except Exception as exc: + self.is_running = False + logger.warning("relay_node_start_failed", error=str(exc)) + raise + + async def stop(self) -> None: + """Stop the relay daemon.""" + if self._poll_task: + self._poll_task.cancel() + self._poll_task = None + + if self._bridge: + try: + await self._bridge.stop() + except Exception: + pass + self._bridge = None + + self.is_running = False + self.peer_id = None + self.multiaddr = None + logger.info("relay_node_stopped") + + # ------------------------------------------------------------------------- + # Status + # ------------------------------------------------------------------------- + + def get_status(self) -> Dict[str, Any]: + """Return current relay node status for the Settings UI.""" + return { + "running": self.is_running, + "peer_id": self.peer_id, + "multiaddr": self.multiaddr, + "peer_count": len(self._peer_status), + "online_peers": sum(1 for p in self._peer_status.values() if p.get("online")), + } + + # ------------------------------------------------------------------------- + # Peer management + # ------------------------------------------------------------------------- + + def get_peers(self) -> List[Dict[str, Any]]: + """ + Return combined peer list: + - Built-in seed relays (read-only, with live status) + - User custom peers (removable, with live status) + """ + peers = [] + + for relay in BUILTIN_RELAYS: + for addr in relay["multiaddrs"]: + status = self._peer_status.get(addr, {}) + peers.append({ + "multiaddr": addr, + "label": relay["label"], + "operator": relay["operator"], + "builtin": True, + "online": status.get("online", None), # None = not yet checked + "latency_ms": status.get("latency_ms", None), + }) + + for addr in self.custom_peers: + status = self._peer_status.get(addr, {}) + peers.append({ + "multiaddr": addr, + "label": addr, + "operator": "custom", + "builtin": False, + "online": status.get("online", None), + "latency_ms": status.get("latency_ms", None), + }) + + return peers + + async def add_peer(self, multiaddr: str) -> bool: + """Add a custom relay peer.""" + if multiaddr in self.custom_peers: + return False + self.custom_peers.append(multiaddr) + if self._bridge: + try: + await self._bridge.connect(multiaddr) + except Exception: + pass + logger.info("relay_peer_added", multiaddr=multiaddr) + return True + + async def remove_peer(self, multiaddr: str) -> bool: + """Remove a custom relay peer.""" + if multiaddr not in self.custom_peers: + return False + self.custom_peers.remove(multiaddr) + logger.info("relay_peer_removed", multiaddr=multiaddr) + return True + + # ------------------------------------------------------------------------- + # Messaging + # ------------------------------------------------------------------------- + + async def send_message( + self, + recipient_qube_id: str, + encrypted_payload: bytes, + ttl_days: int = 7, + ) -> bool: + """ + Send an encrypted message to a recipient Qube. + + Transport fallback: + 1. DHT routing via live p2pd bridge + 2. Store-and-forward queue (held until recipient comes online) + """ + if self._bridge and self.is_running: + try: + # TODO Phase 2: wrap in 2-hop onion before sending + # TODO Phase 3: fall back to Nostr transport if DHT unreachable + topic = f"qube/{recipient_qube_id}/inbox" + await self._bridge.publish(topic, encrypted_payload) + logger.info("relay_message_sent", recipient=recipient_qube_id, bytes=len(encrypted_payload)) + return True + except Exception as exc: + logger.warning("relay_direct_send_failed", recipient=recipient_qube_id, error=str(exc)) + + # Fall back to store-and-forward + import time + ttl_ts = int(time.time()) + ttl_days * 86400 + self.store_forward.enqueue(recipient_qube_id, encrypted_payload, ttl_ts) + logger.info("relay_message_queued", recipient=recipient_qube_id) + return True + + async def get_pending_messages(self, qube_id: str) -> List[Dict[str, Any]]: + """Return and drain store-and-forward messages for a Qube.""" + messages = self.store_forward.dequeue(qube_id) + return [{"payload": m["payload"], "queued_at": m["queued_at"]} for m in messages] + + # ------------------------------------------------------------------------- + # Background peer polling + # ------------------------------------------------------------------------- + + async def _poll_peers_loop(self) -> None: + """Poll all known peers for reachability every 30 s.""" + while self.is_running: + await self._check_all_peers() + await asyncio.sleep(30) + + async def _check_all_peers(self) -> None: + """Ping all peers and update status cache.""" + all_addrs: List[str] = [] + for relay in BUILTIN_RELAYS: + all_addrs.extend(relay["multiaddrs"]) + all_addrs.extend(self.custom_peers) + + for addr in all_addrs: + start = time.monotonic() + online = False + latency_ms = None + try: + if self._bridge: + await asyncio.wait_for(self._bridge.ping(addr), timeout=5.0) + latency_ms = int((time.monotonic() - start) * 1000) + online = True + except Exception: + pass + self._peer_status[addr] = { + "online": online, + "latency_ms": latency_ms, + "last_checked": int(time.time()), + } diff --git a/network/store_forward.py b/network/store_forward.py new file mode 100644 index 0000000..645d4fd --- /dev/null +++ b/network/store_forward.py @@ -0,0 +1,127 @@ +""" +Store-and-Forward Queue — Phase 1 + +JSON file-backed message queue that holds encrypted messages for offline +recipients. Messages are purged after delivery or when TTL expires (default 7 days). + +No new dependencies — pure stdlib JSON + file I/O. +""" + +import json +import os +import time +from pathlib import Path +from typing import Any, Dict, List + +from utils.logging import get_logger + +logger = get_logger(__name__) + +_QUEUE_FILE = "relay_queue.json" + + +class StoreForwardQueue: + """ + Encrypted message queue for offline delivery. + + Storage format (relay_queue.json): + { + "recipient_qube_id": [ + {"payload_hex": "...", "queued_at": 1234567890, "ttl": 1234567890}, + ... + ] + } + """ + + def __init__(self, queue_dir: Path, retention_days: int = 7): + self.queue_file = Path(queue_dir) / _QUEUE_FILE + self.retention_seconds = retention_days * 86400 + self._queue: Dict[str, List[Dict[str, Any]]] = {} + self._load() + + # ------------------------------------------------------------------------- + # Public API + # ------------------------------------------------------------------------- + + def enqueue(self, recipient_id: str, encrypted_payload: bytes, ttl_timestamp: int) -> None: + """Hold an encrypted message until the recipient connects.""" + if recipient_id not in self._queue: + self._queue[recipient_id] = [] + + self._queue[recipient_id].append({ + "payload_hex": encrypted_payload.hex(), + "queued_at": int(time.time()), + "ttl": ttl_timestamp, + }) + self._save() + logger.info("sfq_enqueued", recipient=recipient_id, queue_depth=len(self._queue[recipient_id])) + + def dequeue(self, recipient_id: str) -> List[Dict[str, Any]]: + """ + Return all pending messages for a recipient and remove them from the queue. + Returns list of dicts with keys: payload (bytes), queued_at (int). + """ + if recipient_id not in self._queue: + return [] + + messages = self._queue.pop(recipient_id) + self._save() + + now = int(time.time()) + result = [] + for msg in messages: + if msg["ttl"] >= now: + result.append({ + "payload": bytes.fromhex(msg["payload_hex"]), + "queued_at": msg["queued_at"], + }) + logger.info("sfq_dequeued", recipient=recipient_id, count=len(result)) + return result + + def purge_expired(self) -> int: + """Delete all messages whose TTL has passed. Returns number of messages purged.""" + now = int(time.time()) + total_purged = 0 + + for recipient_id in list(self._queue.keys()): + before = len(self._queue[recipient_id]) + self._queue[recipient_id] = [m for m in self._queue[recipient_id] if m["ttl"] >= now] + purged = before - len(self._queue[recipient_id]) + total_purged += purged + if not self._queue[recipient_id]: + del self._queue[recipient_id] + + if total_purged: + self._save() + logger.info("sfq_purged", count=total_purged) + + return total_purged + + def queue_depth(self, recipient_id: str) -> int: + """Return number of pending messages for a recipient.""" + return len(self._queue.get(recipient_id, [])) + + def total_queued(self) -> int: + """Return total messages across all recipients.""" + return sum(len(msgs) for msgs in self._queue.values()) + + # ------------------------------------------------------------------------- + # Internal persistence + # ------------------------------------------------------------------------- + + def _load(self) -> None: + if not self.queue_file.exists(): + self._queue = {} + return + try: + with open(self.queue_file, "r", encoding="utf-8") as f: + self._queue = json.load(f) + except (json.JSONDecodeError, OSError): + self._queue = {} + + def _save(self) -> None: + try: + with open(self.queue_file, "w", encoding="utf-8") as f: + json.dump(self._queue, f) + except OSError as exc: + logger.warning("sfq_save_failed", error=str(exc)) diff --git a/qubes-gui/src-tauri/src/lib.rs b/qubes-gui/src-tauri/src/lib.rs index b8eaab6..d2e5c61 100644 --- a/qubes-gui/src-tauri/src/lib.rs +++ b/qubes-gui/src-tauri/src/lib.rs @@ -22,6 +22,7 @@ static RATE_LIMITER: Mutex>> = Mutex::new(None); fn get_rate_limit_ms(command: &str) -> Option { match command { "send_message" => Some(500), + "send_direct_p2p_message" => Some(500), "generate_speech" => Some(100), // Reduced from 1000ms to allow faster TTS prefetch "create_qube" => Some(5000), "anchor_session" => Some(2000), @@ -5236,9 +5237,152 @@ async fn process_p2p_message(app_handle: AppHandle, } +// ============================================================================= +// RELAY NODE COMMANDS — Phase 1 +// ============================================================================= + +/// Start the local P2P relay node +#[tauri::command] +async fn init_relay_node(app_handle: AppHandle, user_id: String, password: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id]; + let mut secrets = HashMap::new(); + secrets.insert("password", password.as_str()); + sidecar_execute_with_retry("init-relay-node", args, secrets, Some(&app_handle), None).await +} + +/// Stop the local P2P relay node +#[tauri::command] +async fn stop_relay_node(app_handle: AppHandle, user_id: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id]; + sidecar_execute_with_retry("stop-relay-node", args, HashMap::new(), Some(&app_handle), None).await +} + +/// Get relay node status (running, peer_id, peer_count, online_peers) +#[tauri::command] +async fn get_relay_status(app_handle: AppHandle, user_id: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id]; + sidecar_execute_with_retry("get-relay-status", args, HashMap::new(), Some(&app_handle), None).await +} + +/// Get list of relay peers with live reachability status +#[tauri::command] +async fn get_relay_peers(app_handle: AppHandle, user_id: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id]; + sidecar_execute_with_retry("get-relay-peers", args, HashMap::new(), Some(&app_handle), None).await +} + +/// Add a custom relay peer +#[tauri::command] +async fn add_relay_peer(app_handle: AppHandle, user_id: String, multiaddr: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id, multiaddr]; + sidecar_execute_with_retry("add-relay-peer", args, HashMap::new(), Some(&app_handle), None).await +} + +/// Remove a custom relay peer +#[tauri::command] +async fn remove_relay_peer(app_handle: AppHandle, user_id: String, multiaddr: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id, multiaddr]; + sidecar_execute_with_retry("remove-relay-peer", args, HashMap::new(), Some(&app_handle), None).await +} + +/// Update relay preferences +#[tauri::command] +async fn update_relay_preferences(app_handle: AppHandle, user_id: String, password: String, prefs_json: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id, prefs_json]; + let mut secrets = HashMap::new(); + secrets.insert("password", password.as_str()); + sidecar_execute_with_retry("update-relay-preferences", args, secrets, Some(&app_handle), None).await +} + +/// Send an encrypted direct P2P message via relay +#[tauri::command] +async fn send_direct_p2p_message( + app_handle: AppHandle, + user_id: String, + qube_id: String, + recipient_qube_id: String, + recipient_pub_key: String, + message: String, + password: String, +) -> Result { + check_rate_limit("send_direct_p2p_message")?; + validate_identifier(&user_id, "user_id")?; + validate_identifier(&qube_id, "qube_id")?; + let args = vec![user_id, qube_id, recipient_qube_id, recipient_pub_key, message]; + let mut secrets = HashMap::new(); + secrets.insert("password", password.as_str()); + sidecar_execute_with_retry("send-direct-p2p-message", args, secrets, Some(&app_handle), None).await +} + +/// Drain store-and-forward queue for a Qube +#[tauri::command] +async fn get_direct_p2p_messages(app_handle: AppHandle, user_id: String, qube_id: String, password: String) -> Result { + validate_identifier(&user_id, "user_id")?; + validate_identifier(&qube_id, "qube_id")?; + let args = vec![user_id, qube_id]; + let mut secrets = HashMap::new(); + secrets.insert("password", password.as_str()); + sidecar_execute_with_retry("get-direct-p2p-messages", args, secrets, Some(&app_handle), None).await +} + +/// Check if a newer relay bundle is available (Phase 5) +#[tauri::command] +async fn check_relay_bundle_update(app_handle: AppHandle, user_id: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id]; + sidecar_execute_with_retry("check-relay-bundle-update", args, HashMap::new(), Some(&app_handle), None).await +} + +/// Download and apply the latest relay bundle (Phase 5) +#[tauri::command] +async fn update_relay_bundle(app_handle: AppHandle, user_id: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id]; + sidecar_execute_with_retry("update-relay-bundle", args, HashMap::new(), Some(&app_handle), None).await +} + +// ============================================================================= +// ENDPOINT PREFERENCES COMMANDS +// ============================================================================= + +/// Get network endpoint preferences +#[tauri::command] +async fn get_endpoint_preferences(app_handle: AppHandle, user_id: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id]; + sidecar_execute_with_retry("get-endpoint-preferences", args, HashMap::new(), Some(&app_handle), None).await +} + +/// Update network endpoint preferences +#[tauri::command] +async fn update_endpoint_preferences(app_handle: AppHandle, user_id: String, password: String, prefs_json: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id, prefs_json]; + let mut secrets = HashMap::new(); + secrets.insert("password", password.as_str()); + sidecar_execute_with_retry("update-endpoint-preferences", args, secrets, Some(&app_handle), None).await +} + +/// Reset endpoint preferences to defaults +#[tauri::command] +async fn reset_endpoint_preferences(app_handle: AppHandle, user_id: String, password: String) -> Result { + validate_identifier(&user_id, "user_id")?; + let args = vec![user_id]; + let mut secrets = HashMap::new(); + secrets.insert("password", password.as_str()); + sidecar_execute_with_retry("reset-endpoint-preferences", args, secrets, Some(&app_handle), None).await +} + /// Send an introduction request to another Qube #[tauri::command] -async fn send_introduction(app_handle: AppHandle, +async fn send_introduction(app_handle: AppHandle, user_id: String, qube_id: String, to_commitment: String, @@ -7916,6 +8060,22 @@ pub fn run() { continue_p2p_conversation, inject_p2p_block, send_p2p_user_message, + // Relay Node (Phase 1) + init_relay_node, + stop_relay_node, + get_relay_status, + get_relay_peers, + add_relay_peer, + remove_relay_peer, + update_relay_preferences, + send_direct_p2p_message, + get_direct_p2p_messages, + check_relay_bundle_update, + update_relay_bundle, + // Endpoint Preferences + get_endpoint_preferences, + update_endpoint_preferences, + reset_endpoint_preferences, // Setup Wizard get_bundle_dir, check_first_run, diff --git a/qubes-gui/src/components/tabs/SettingsTab.tsx b/qubes-gui/src/components/tabs/SettingsTab.tsx index 9f5e296..ab1bdc6 100644 --- a/qubes-gui/src/components/tabs/SettingsTab.tsx +++ b/qubes-gui/src/components/tabs/SettingsTab.tsx @@ -77,6 +77,40 @@ interface MemoryConfig { relationship_weight: number; } +interface RelayPeer { + multiaddr: string; + label: string; + operator: string; + builtin: boolean; + online: boolean | null; + latency_ms: number | null; +} + +interface RelayStatus { + running: boolean; + peer_id: string | null; + multiaddr: string | null; + peer_count: number; + online_peers: number; +} + +interface RelayPreferences { + relay_enabled: boolean; + relay_listen_port: number; + relay_max_connections: number; + relay_retention_days: number; + relay_custom_peers: string[]; + p2pd_binary_path: string | null; +} + +interface EndpointPreferences { + fulcrum_nodes: string[]; + nostr_relays: string[]; + cauldron_indexer: string; + thorchain_midgard: string; + meta_icon_service: string; +} + export const SettingsTab: React.FC = () => { const { userId, password, autoLockEnabled, autoLockTimeout, setAutoLockSettings } = useAuth(); const { invalidateCache, loadChainState } = useChainState(); @@ -225,8 +259,105 @@ export const SettingsTab: React.FC = () => { security: true, celebrationSettings: true, softwareUpdates: true, + relayNode: true, + endpoints: true, }); + // Relay Node state + const [relayStatus, setRelayStatus] = useState({ running: false, peer_id: null, multiaddr: null, peer_count: 0, online_peers: 0 }); + const [relayPeers, setRelayPeers] = useState([]); + const [relayPrefs, setRelayPrefs] = useState({ relay_enabled: true, relay_listen_port: 0, relay_max_connections: 50, relay_retention_days: 7, relay_custom_peers: [], p2pd_binary_path: null }); + const [relayLoading, setRelayLoading] = useState(false); + const [relayBundleStatus, setRelayBundleStatus] = useState(null); + const [newRelayPeer, setNewRelayPeer] = useState(''); + + // Endpoints state + const [endpointPrefs, setEndpointPrefs] = useState({ fulcrum_nodes: [], nostr_relays: [], cauldron_indexer: '', thorchain_midgard: '', meta_icon_service: '' }); + const [endpointsDirty, setEndpointsDirty] = useState(false); + const [savingEndpoints, setSavingEndpoints] = useState(false); + const [endpointSaveMsg, setEndpointSaveMsg] = useState(null); + + const loadRelayData = async () => { + try { + const [statusRes, peersRes] = await Promise.all([ + invoke('get_relay_status', { userId }), + invoke('get_relay_peers', { userId }), + ]); + if (statusRes?.success !== false) setRelayStatus(statusRes); + if (peersRes?.success !== false && peersRes?.peers) setRelayPeers(peersRes.peers); + } catch {} + }; + + const handleStartRelay = async () => { + setRelayLoading(true); + try { + const res = await invoke('init_relay_node', { userId, password }); + if (res?.success !== false) { setRelayStatus({ ...relayStatus, running: true, ...res }); } + } catch {} + setRelayLoading(false); + await loadRelayData(); + }; + + const handleStopRelay = async () => { + setRelayLoading(true); + try { await invoke('stop_relay_node', { userId }); setRelayStatus(s => ({ ...s, running: false, peer_id: null, multiaddr: null })); } catch {} + setRelayLoading(false); + }; + + const handleAddRelayPeer = async () => { + if (!newRelayPeer.trim()) return; + await invoke('add_relay_peer', { userId, multiaddr: newRelayPeer.trim() }); + setNewRelayPeer(''); + await loadRelayData(); + }; + + const handleRemoveRelayPeer = async (multiaddr: string) => { + await invoke('remove_relay_peer', { userId, multiaddr }); + await loadRelayData(); + }; + + const handleCheckBundleUpdate = async () => { + setRelayBundleStatus('Checking...'); + try { + const res = await invoke('check_relay_bundle_update', { userId }); + setRelayBundleStatus(res?.update_available ? `Update available: ${res.latest_version}` : `Up to date (${res?.current_version || 'none'})`); + } catch (e: any) { setRelayBundleStatus(`Error: ${e}`); } + }; + + const handleUpdateBundle = async () => { + setRelayBundleStatus('Updating...'); + try { + const res = await invoke('update_relay_bundle', { userId }); + setRelayBundleStatus(res?.message || (res?.success ? 'Updated!' : 'Failed')); + } catch (e: any) { setRelayBundleStatus(`Error: ${e}`); } + }; + + const loadEndpoints = async () => { + try { + const res = await invoke('get_endpoint_preferences', { userId }); + if (res?.endpoints) setEndpointPrefs(res.endpoints); + } catch {} + }; + + const handleSaveEndpoints = async () => { + setSavingEndpoints(true); + setEndpointSaveMsg(null); + try { + const prefs_json = JSON.stringify(endpointPrefs); + await invoke('update_endpoint_preferences', { userId, password, prefsJson: prefs_json }); + setEndpointsDirty(false); + setEndpointSaveMsg('Saved — changes apply on page reload'); + } catch (e: any) { setEndpointSaveMsg(`Error: ${e}`); } + setSavingEndpoints(false); + }; + + const handleResetEndpoints = async () => { + try { + const res = await invoke('reset_endpoint_preferences', { userId, password }); + if (res?.endpoints) { setEndpointPrefs(res.endpoints); setEndpointsDirty(false); setEndpointSaveMsg('Reset to defaults — changes apply on page reload'); } + } catch {} + }; + const handleChangePassword = async () => { setChangePwError(null); setChangePwSuccess(null); @@ -2840,6 +2971,211 @@ export const SettingsTab: React.FC = () => { )} + {/* =========================================================== */} + {/* Relay Node Panel */} + {/* =========================================================== */} + + + + {!collapsedPanels.relayNode && ( + <> +

+ Your desktop app automatically acts as a P2P relay node. Every user who runs Qubes contributes relay capacity to the network — no configuration required. +

+ + {/* LOCAL RELAY */} +
+

LOCAL RELAY

+
+ + {relayStatus.running ? 'Running' : 'Stopped'} + {relayStatus.running && relayStatus.peer_count !== undefined && ( + {relayStatus.online_peers}/{relayStatus.peer_count} peers online + )} +
+ + {relayStatus.running && relayStatus.peer_id && ( +
+

Peer ID

+

{relayStatus.peer_id}

+
+ )} + + + + {/* p2pd binary path */} +
+

p2pd binary path (leave blank = use bundled)

+ setRelayPrefs(p => ({ ...p, p2pd_binary_path: e.target.value || null }))} + /> +
+ +
+ + + +
+ +
+ ▶ Start + ■ Stop +
+
+ + {/* REMOTE RELAYS */} +
+

REMOTE RELAYS

+ + {/* Seed relays */} +

SEED RELAYS (BitFaced-operated)

+
+ {relayPeers.filter(p => p.builtin).slice(0, 3).map((peer, i) => ( +
+ + {peer.label} + {peer.latency_ms !== null && {peer.latency_ms}ms} + {peer.online === false && offline} +
+ ))} + {relayPeers.filter(p => p.builtin).length === 0 && ( +

Open the relay to see seed relay status

+ )} +
+ + {/* Custom relays */} +

CUSTOM RELAYS

+
+ {relayPeers.filter(p => !p.builtin).map((peer, i) => ( +
+ + {peer.multiaddr} + +
+ ))} +
+
+ setNewRelayPeer(e.target.value)} + onKeyDown={e => e.key === 'Enter' && handleAddRelayPeer()} /> + Add +
+
+ + {/* RELAY BUNDLE */} +
+

RELAY BUNDLE

+

Update the relay bundle (p2pd binary, relay list) independently from the main app.

+
+ Check Update + Update Bundle +
+ {relayBundleStatus &&

{relayBundleStatus}

} +
+ + )} +
+ + {/* =========================================================== */} + {/* Endpoints Panel */} + {/* =========================================================== */} + + + + {!collapsedPanels.endpoints && ( + <> +

+ Configure network endpoints for BCH nodes, Nostr relays, and indexing services. +

+ + {/* Green terminal-style endpoint fields */} +
+ + {/* // ENDPOINTS header */} +

// ENDPOINTS

+ +
+

FULCRUM / ELECTRUM NODES

+