diff --git a/Taskfile.yml b/Taskfile.yml index c3aaf0957..9cf194e5c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -71,10 +71,74 @@ tasks: - echo "MSIX packaging not yet implemented for CEF host. Old Tauri script was removed." package:macos: - desc: "[TODO] Package the application for macOS (CEF). Not yet implemented." + desc: Package the application for macOS (.app + DMG). Outputs to ~/Desktop. platforms: [darwin] + deps: [build:frontend, build:backend, cef:build] cmds: - - echo "macOS CEF packaging not yet implemented. Use cef:package:portable on Windows." + - task: cef:bundle + - | + VERSION=$(node -p "require('./package.json').version") + ARCH="arm64" + APP="dist/AgentMux.app" + MACOS="$APP/Contents/MacOS" + FRAMEWORKS="$APP/Contents/Frameworks" + + # Scaffold .app structure + rm -rf "$APP" + mkdir -p "$MACOS" "$FRAMEWORKS" "$APP/Contents/Resources" + + # CEF host binary (CFBundleExecutable) + cp dist/cef/agentmux-cef "$MACOS/agentmux-cef" + chmod +x "$MACOS/agentmux-cef" + + # CEF framework — library_loader resolves {exe_dir}/../Frameworks/ + # which maps to Contents/MacOS/../Frameworks = Contents/Frameworks/ ✓ + rsync -a "dist/Frameworks/Chromium Embedded Framework.framework" "$FRAMEWORKS/" + + # Backend sidecar — versioned name in exe_dir (sidecar.rs lookup order 1) + cp "dist/bin/agentmux-srv-${VERSION}-darwin.${ARCH}" \ + "$MACOS/agentmux-srv-${VERSION}-darwin.${ARCH}" + chmod +x "$MACOS/agentmux-srv-${VERSION}-darwin.${ARCH}" + + # Built frontend — IPC server serves from {exe_dir}/frontend/ + cp -r dist/frontend "$MACOS/frontend" + + # App icon + [ -f build/icon.icns ] && \ + cp build/icon.icns "$APP/Contents/Resources/AgentMux.icns" + + # Info.plist + BUNDLE_ID="ai.agentmux.app.v$(echo $VERSION | tr '.' '-')" + printf '%s\n' \ + '' \ + '' \ + '' \ + " CFBundleIdentifier ${BUNDLE_ID}" \ + ' CFBundleName AgentMux' \ + ' CFBundleDisplayName AgentMux' \ + ' CFBundleExecutable agentmux-cef' \ + " CFBundleVersion ${VERSION}" \ + " CFBundleShortVersionString ${VERSION}" \ + ' CFBundleIconFile AgentMux' \ + ' LSMinimumSystemVersion 12.0' \ + ' NSHighResolutionCapable ' \ + ' NSRequiresAquaSystemAppearance ' \ + ' com.apple.security.cs.allow-jit ' \ + ' com.apple.security.cs.allow-unsigned-executable-memory ' \ + ' com.apple.security.cs.disable-library-validation ' \ + '' \ + > "$APP/Contents/Info.plist" + echo "✓ Built AgentMux.app (v${VERSION})" + + - | + VERSION=$(node -p "require('./package.json').version") + xattr -cr dist/AgentMux.app 2>/dev/null || true + DMG="dist/AgentMux_${VERSION}_aarch64.dmg" + hdiutil create -volname "AgentMux ${VERSION}" \ + -srcfolder dist/AgentMux.app -ov -format UDZO "$DMG" + xattr -d com.apple.quarantine "$DMG" 2>/dev/null || true + cp "$DMG" ~/Desktop/ + echo "✓ DMG → ~/Desktop/AgentMux_${VERSION}_aarch64.dmg" package:portable:linux: @@ -431,7 +495,44 @@ tasks: internal: true platforms: [darwin] cmds: - - echo "macOS CEF bundling not yet implemented" + - | + VERSION=$(node -p "require('./package.json').version") + ARCH="arm64" + + # 1. Locate CEF framework in build output + CEF_FW=$(find target -type d -name "Chromium Embedded Framework.framework" \ + -path "*/cef-dll-sys*/out/*" 2>/dev/null | head -1) + if [ -z "$CEF_FW" ]; then + echo "❌ Chromium Embedded Framework.framework not found — run cef:build first" + exit 1 + fi + echo "Found CEF framework: $CEF_FW" + + # 2. Copy framework to dist/Frameworks/ + # library_loader resolves {exe_dir}/../Frameworks/ so it must be one level + # above dist/cef/ — i.e. dist/Frameworks/ + mkdir -p dist/Frameworks + rsync -a --delete "$CEF_FW" dist/Frameworks/ + + # 3. Strip all non-English lproj dirs from framework (~50 MB saved) + find "dist/Frameworks/Chromium Embedded Framework.framework/Resources" \ + -maxdepth 1 -name "*.lproj" ! -name "en.lproj" -exec rm -rf {} + 2>/dev/null || true + + echo "✓ CEF framework → dist/Frameworks/" + + - | + VERSION=$(node -p "require('./package.json').version") + ARCH="arm64" + + # 4. Versioned sidecar (sidecar.rs looks for {exe_dir}/agentmux-srv-{VERSION}-darwin.arm64) + cp "dist/bin/agentmux-srv-${VERSION}-darwin.${ARCH}" \ + "dist/cef/agentmux-srv-${VERSION}-darwin.${ARCH}" + + # 5. Built frontend (IPC server serves from {exe_dir}/frontend/) + rm -rf dist/cef/frontend + cp -r dist/frontend dist/cef/frontend + + echo "✓ Bundled macOS runtime to dist/cef/" cef:bundle:linux: internal: true diff --git a/agentmux-cef/src/app.rs b/agentmux-cef/src/app.rs index e4029bd63..7af77f21a 100644 --- a/agentmux-cef/src/app.rs +++ b/agentmux-cef/src/app.rs @@ -262,6 +262,17 @@ wrap_app! { let rpl_key = CefString::from("renderer-process-limit"); let rpl_val = CefString::from("1"); cmd.append_switch_with_value(Some(&rpl_key), Some(&rpl_val)); + + // Bypass macOS Keychain for Chromium's OSCrypt/SafeStorage. + // Without this, Chromium prompts the user to allow keychain + // access on every launch to store a cookie-encryption key. + // AgentMux doesn't store browser passwords so mock keychain + // is safe and eliminates the OS security dialog entirely. + #[cfg(target_os = "macos")] + { + let mk_key = CefString::from("use-mock-keychain"); + cmd.append_switch(Some(&mk_key)); + } } } diff --git a/agentmux-cef/src/client.rs b/agentmux-cef/src/client.rs index e40f7f7d8..95a01c19b 100644 --- a/agentmux-cef/src/client.rs +++ b/agentmux-cef/src/client.rs @@ -10,6 +10,13 @@ use cef::*; use std::sync::Arc; use parking_lot::Mutex; +// The native OS key event type differs per platform. On macOS/Linux it is an +// opaque `*mut u8` (NSEvent* / XEvent*); on Windows it is `cef::sys::MSG`. +#[cfg(windows)] +type NativeKeyEvent = cef::sys::MSG; +#[cfg(not(windows))] +type NativeKeyEvent = u8; + use crate::state::AppState; /// Write a debug line to `%TEMP%\agentmux-close-debug.txt`. @@ -673,7 +680,7 @@ wrap_keyboard_handler! { &self, _browser: Option<&mut Browser>, event: Option<&KeyEvent>, - _os_event: Option<&mut cef::sys::MSG>, + _os_event: *mut NativeKeyEvent, is_keyboard_shortcut: Option<&mut ::std::os::raw::c_int>, ) -> ::std::os::raw::c_int { if let Some(ev) = event { diff --git a/agentmux-cef/src/main.rs b/agentmux-cef/src/main.rs index c0f67dcea..81e880873 100644 --- a/agentmux-cef/src/main.rs +++ b/agentmux-cef/src/main.rs @@ -109,6 +109,14 @@ fn main() { tracing::info!("Initializing CEF browser process"); + // macOS 26 Tahoe compat: CEF 146 calls a private NSApplication selector during + // NSDraggingSession setup that was removed in macOS 26. Swizzle + // doesNotRecognizeSelector: on NSApplication to log the selector name and + // return without throwing NSInvalidArgumentException, allowing drag to proceed. + // See: docs/investigations/tab-drag-tearoff-crash-macos.md + #[cfg(target_os = "macos")] + unsafe { patch_nsapp_unrecognized_selector() }; + // Single-instance check: if another instance of the same version is // running, send it a "new window" request via its IPC server and exit. // Uses a named mutex for detection and a port file for communication. @@ -207,14 +215,31 @@ fn main() { .and_then(|p| p.parent().map(|d| d.to_path_buf())) .unwrap_or_default(); let runtime_dir = exe_dir.join("runtime"); - let base_dir = if runtime_dir.exists() { + let _base_dir = if runtime_dir.exists() { runtime_dir } else { // Dev mode: resources are flat alongside the exe in dist/cef/ exe_dir.clone() }; - let resources_dir = CefString::from(base_dir.to_str().unwrap_or("")); - let locales_dir = CefString::from(base_dir.join("locales").to_str().unwrap_or("")); + // On macOS, pak files and locale paks live inside the CEF framework's + // Resources/ directory — not alongside the executable. The framework is + // at {exe_dir}/../Frameworks/ (resolved by library_loader above). + // Pass that path for both resources_dir and locales_dir so CEF finds + // chrome_*.pak, resources.pak, icudtl.dat, and the *.lproj/locale.pak files. + #[cfg(target_os = "macos")] + let (resources_dir, locales_dir) = { + let fw_resources = exe_dir + .join("../Frameworks/Chromium Embedded Framework.framework/Resources"); + // canonicalize() resolves ".." so CEF receives a clean absolute path. + let fw_resources = fw_resources.canonicalize().unwrap_or(fw_resources); + let s = fw_resources.to_str().unwrap_or("").to_owned(); + (CefString::from(s.as_str()), CefString::from(s.as_str())) + }; + #[cfg(not(target_os = "macos"))] + let (resources_dir, locales_dir) = ( + CefString::from(_base_dir.to_str().unwrap_or("")), + CefString::from(_base_dir.join("locales").to_str().unwrap_or("")), + ); // Reuse data_dir from single-instance check as CEF cache path. // Remove stale lockfile from a previous killed run. @@ -227,6 +252,14 @@ fn main() { let cache_dir = CefString::from(data_dir.to_str().unwrap_or("")); // Configure CEF settings. + // On macOS, tell CEF exactly where the framework lives so it can load ICU + // and register the bundle correctly — required when running outside a .app. + #[cfg(target_os = "macos")] + let framework_dir = { + let p = exe_dir.join("../Frameworks/Chromium Embedded Framework.framework"); + p.canonicalize().unwrap_or(p) + }; + let settings = Settings { no_sandbox: 1, background_color: 0xFF000000, @@ -238,6 +271,8 @@ fn main() { browser_subprocess_path: CefString::from( std::env::current_exe().unwrap().to_str().unwrap_or("") ), + #[cfg(target_os = "macos")] + framework_dir_path: CefString::from(framework_dir.to_str().unwrap_or("")), ..Default::default() }; @@ -290,6 +325,114 @@ fn main() { tracing::info!("AgentMux CEF host shutdown complete"); } +/// macOS 26 Tahoe compat: CEF 146 calls private NSApplication selectors (e.g. +/// `isHandlingSendEvent`) during NSDraggingSession setup that were removed in macOS 26. +/// +/// The correct fix is to hook `+[NSApplication resolveInstanceMethod:]` — the earliest +/// point in the ObjC dispatch chain — so missing selectors get a void stub before the +/// forwarding machinery (`___forwarding___`) is invoked. Swizzling `doesNotRecognizeSelector:` +/// is wrong here: that method is called FROM inside `___forwarding___`, and returning +/// normally from it (without throwing) corrupts the forwarding state and causes a second +/// crash inside `___forwarding___` itself. +/// +/// Return-type-aware stubs: `isHandlingSendEvent` and similar BOOL guard getters must +/// return 0 (NO). A void stub leaves x0 = self (truthy), causing CEF to think the app +/// is already handling a send event and skip normal event routing — breaking window drag. +/// All other unknown selectors get a void stub, which is safe. +/// +/// Safety: Called once before CEF initializes. NSApplication is a singleton; adding a +/// `resolveInstanceMethod:` implementation on its metaclass is safe at startup. +#[cfg(target_os = "macos")] +unsafe fn patch_nsapp_unrecognized_selector() { + use std::ffi::{c_char, c_void}; + + type Id = *mut c_void; + type Sel = *const c_void; + type Class = *mut c_void; + + extern "C" { + fn objc_getClass(name: *const c_char) -> Class; + fn object_getClass(obj: Id) -> Class; // on a Class obj → returns metaclass + fn sel_registerName(name: *const c_char) -> Sel; + fn sel_getName(sel: Sel) -> *const c_char; + fn class_addMethod( + cls: Class, + sel: Sel, + imp: usize, + types: *const c_char, + ) -> u8; // BOOL + } + + // Generic void stub for unknown selectors that return nothing (or whose + // return value is not used by callers). + unsafe extern "C" fn void_stub(_self: Id, _cmd: Sel) {} + + // BOOL stub returning 0 (NO) for guard-style getters. On ARM64, a void stub + // leaves x0 = self (non-nil = truthy), which breaks callers like CEF's + // sendEvent: guard that skips event routing when isHandlingSendEvent returns YES. + unsafe extern "C" fn bool_no_stub(_self: Id, _cmd: Sel) -> u8 { 0 } + + // +resolveInstanceMethod: injected into NSApplication metaclass. + // Called by the ObjC runtime the first time an unknown selector is sent to + // an NSApplication instance — before ___forwarding___ is ever entered. + // We add a typed stub and return YES so the runtime retries the send. + unsafe extern "C" fn resolve_instance_method_impl( + cls: Class, + _cmd: Sel, + sel: Sel, + ) -> u8 { + let name = { + let ptr = sel_getName(sel); + if ptr.is_null() { "".to_owned() } + else { std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned() } + }; + + // These selectors return BOOL and callers act on the value. + // Returning truthy (garbage from a void stub) breaks event routing and + // prevents window drag from receiving mouse events. + const BOOL_NO_SELECTORS: &[&str] = &[ + "isHandlingSendEvent", + "isSendingEvent", + ]; + + if BOOL_NO_SELECTORS.contains(&name.as_str()) { + tracing::warn!(selector = %name, "macOS 26 compat: adding BOOL(NO) stub"); + class_addMethod(cls, sel, bool_no_stub as usize, b"c@:\0".as_ptr() as _); + } else { + tracing::warn!(selector = %name, "macOS 26 compat: adding void stub"); + class_addMethod(cls, sel, void_stub as usize, b"v@:\0".as_ptr() as _); + } + 1 // YES — resolved; runtime retries the original send + } + + let cls = objc_getClass(b"NSApplication\0".as_ptr() as _); + if cls.is_null() { + tracing::warn!("macOS 26 compat: NSApplication class not found"); + return; + } + + // The metaclass is the "class object" of a class; class methods live there. + let metacls = object_getClass(cls as Id); + if metacls.is_null() { + tracing::warn!("macOS 26 compat: NSApplication metaclass not found"); + return; + } + + let sel = sel_registerName(b"resolveInstanceMethod:\0".as_ptr() as _); + // "c@::" = BOOL return, id (Class), SEL (cmd), SEL (queried selector) + let added = class_addMethod( + metacls, + sel, + resolve_instance_method_impl as usize, + b"c@::\0".as_ptr() as _, + ); + if added != 0 { + tracing::info!("macOS 26 compat: injected resolveInstanceMethod: into NSApplication metaclass"); + } else { + tracing::warn!("macOS 26 compat: class_addMethod failed (method already exists?)"); + } +} + /// Initialize tracing with dual output: rolling daily log file + human-readable stderr. /// Returns a guard that must be held for the lifetime of the process to ensure log flushing. fn init_logging() -> tracing_appender::non_blocking::WorkerGuard { diff --git a/package-lock.json b/package-lock.json index 7356474a5..0d00da79b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -847,7 +847,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -864,7 +863,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -881,7 +879,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -898,7 +895,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -915,7 +911,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -932,7 +927,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -949,7 +943,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -966,7 +959,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -983,7 +975,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1000,7 +991,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1017,7 +1007,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1034,7 +1023,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1051,7 +1039,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1068,7 +1055,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1085,7 +1071,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1102,7 +1087,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1119,7 +1103,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1136,7 +1119,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1153,7 +1135,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1170,7 +1151,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1187,7 +1167,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1204,7 +1183,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1221,7 +1199,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1238,7 +1215,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1255,7 +1231,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1272,7 +1247,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1649,7 +1623,6 @@ "version": "2.5.6", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -1689,7 +1662,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1710,7 +1682,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1731,7 +1702,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1752,7 +1722,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1773,7 +1742,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1794,7 +1762,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1815,7 +1782,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1836,7 +1802,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1857,7 +1822,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1878,7 +1842,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1899,7 +1862,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1920,7 +1882,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1941,7 +1902,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2156,7 +2116,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2170,7 +2129,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2184,7 +2142,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2198,7 +2155,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2212,7 +2168,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2226,7 +2181,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2240,7 +2194,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2254,7 +2207,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2268,7 +2220,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2282,7 +2233,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2296,7 +2246,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2310,7 +2259,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2324,7 +2272,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2338,7 +2285,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2352,7 +2298,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2366,7 +2311,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2380,7 +2324,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2394,7 +2337,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2408,7 +2350,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2422,7 +2363,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2436,7 +2376,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2450,7 +2389,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2464,7 +2402,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2478,7 +2415,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2492,7 +2428,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4139,7 +4074,7 @@ "version": "22.19.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.13.tgz", "integrity": "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -4166,7 +4101,6 @@ "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -4176,7 +4110,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -5199,7 +5133,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -6052,7 +5986,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -6194,7 +6128,7 @@ "version": "0.27.3", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -6552,7 +6486,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -6646,7 +6579,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -6692,7 +6624,7 @@ "version": "4.13.6", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -7275,7 +7207,7 @@ "version": "5.1.4", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/import-fresh": { @@ -7409,7 +7341,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7429,7 +7361,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -7600,7 +7532,7 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -7857,7 +7789,7 @@ "version": "1.31.1", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", "integrity": "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==", - "dev": true, + "devOptional": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -7890,7 +7822,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7911,7 +7842,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7932,7 +7862,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7953,7 +7882,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7974,7 +7902,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7995,7 +7922,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8016,7 +7942,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8037,7 +7962,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8058,7 +7982,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8079,7 +8002,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8100,7 +8022,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9349,7 +9270,6 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, "funding": [ { "type": "github", @@ -9399,7 +9319,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, "license": "MIT", "optional": true }, @@ -9678,7 +9597,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -9733,7 +9651,6 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -10011,7 +9928,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -10350,7 +10267,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, + "devOptional": true, "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" @@ -10416,7 +10333,6 @@ "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -10518,7 +10434,7 @@ "version": "1.97.2", "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.2.tgz", "integrity": "sha512-y5LWb0IlbO4e97Zr7c3mlpabcbBtS+ieiZ9iwDooShpFKWXf62zz5pEPdwrLYm+Bxn1fnbwFGzHuCLSA9tBmrw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.0", @@ -11158,7 +11074,6 @@ "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -11374,7 +11289,7 @@ "version": "4.21.0", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "esbuild": "~0.27.0", @@ -11394,7 +11309,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -11466,7 +11380,7 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/unified": { @@ -11695,7 +11609,6 @@ "version": "6.4.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -11862,7 +11775,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -11879,7 +11791,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -11896,7 +11807,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -11913,7 +11823,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -11930,7 +11839,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -11947,7 +11855,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -11964,7 +11871,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -11981,7 +11887,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -11998,7 +11903,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12015,7 +11919,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12032,7 +11935,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12049,7 +11951,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12066,7 +11967,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12083,7 +11983,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12100,7 +11999,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12117,7 +12015,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12134,7 +12031,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12151,7 +12047,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12168,7 +12063,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12185,7 +12079,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12202,7 +12095,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12219,7 +12111,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12236,7 +12127,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12253,7 +12143,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12270,7 +12159,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12287,7 +12175,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -12301,7 +12188,6 @@ "version": "0.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -12343,7 +12229,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true,