diff --git a/api/src/lib.rs b/api/src/lib.rs index ab4956b..7d2b6ae 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -1,4 +1,4 @@ -use image::{DynamicImage, ImageError, ImageOutputFormat}; +use image::{DynamicImage, ImageBuffer, ImageError, ImageOutputFormat, Rgb}; use std::io::Cursor; use wasm_bindgen::prelude::*; @@ -22,8 +22,103 @@ pub fn to_png(image: &[u8]) -> Result, JsValue> { read_and_encode(image, |img| img) } +#[wasm_bindgen] +pub fn clip_pixels_with_percentiles( + image_data: &[u8], + low_percentile: Option, + high_percentile: Option, +) -> Result, JsValue> { + if let Some(lp) = low_percentile { + if lp < 0.0 || lp > 100.0 { + return Err(JsValue::from_str("Percentile must be between 0 and 100")); + } + } + if let Some(hp) = high_percentile { + if hp < 0.0 || hp > 100.0 { + return Err(JsValue::from_str("Percentile must be between 0 and 100")); + } + } + + if let (Some(lp), Some(hp)) = (low_percentile, high_percentile) { + if lp > hp { + return Err(JsValue::from_str("low_percentile must be <= high_percentile")); + } + } + + let img = image::load_from_memory(image_data) + .map_err(|e| JsValue::from_str(&format!("Image decode error: {}", e)))?; + + let rgb8: image::RgbImage = img.to_rgb8(); + let (width, height) = rgb8.dimensions(); + let mut pixels: Vec = rgb8.into_raw(); + + if low_percentile.is_none() && high_percentile.is_none() { + let buf: ImageBuffer, Vec> = + ImageBuffer::from_raw(width, height, pixels.clone()) + .ok_or_else(|| JsValue::from_str("Buffer length mismatch"))?; + let dyn_img = DynamicImage::ImageRgb8(buf); + let mut out = Cursor::new(Vec::new()); + dyn_img + .write_to(&mut out, ImageOutputFormat::Png) + .map_err(|e| JsValue::from_str(&format!("PNG encode error: {}", e)))?; + return Ok(out.into_inner()); + } + + let mut flat_for_low: Vec = pixels.clone(); + let mut flat_for_high: Vec = pixels.clone(); + + // Compute low_cutoff and high_cutoff + let low_cutoff: Option = percentile_cutoff(&mut flat_for_low, low_percentile); + let high_cutoff: Option = percentile_cutoff(&mut flat_for_high, high_percentile); + + let low_val: u8 = low_cutoff.unwrap_or(0); + let high_val: u8 = high_cutoff.unwrap_or(255); + + pixels.iter_mut().for_each(|channel_byte| { + if *channel_byte < low_val { + *channel_byte = low_val; + } else if *channel_byte > high_val { + *channel_byte = high_val; + } + }); + + let clip_buf: ImageBuffer, Vec> = + ImageBuffer::from_raw(width, height, pixels) + .ok_or_else(|| JsValue::from_str("Buffer length mismatch after clipping"))?; + let dyn_img = DynamicImage::ImageRgb8(clip_buf); + + let mut output = Cursor::new(Vec::new()); + dyn_img + .write_to(&mut output, ImageOutputFormat::Png) + .map_err(|e| JsValue::from_str(&format!("PNG encode error: {}", e)))?; + Ok(output.into_inner()) +} + // Helpers +fn percentile_cutoff(flat_pixels: &mut [u8], pct: Option) -> Option { + if let Some(p) = pct { + let p = if p < 0.0 { 0.0 } else if p > 100.0 { 100.0 } else { p }; + + let total = flat_pixels.len(); + if total == 0 { + return None; + } + let idx_f = (p / 100.0) * (total as f32); + let mut idx = idx_f.floor() as usize; + if idx >= total { + idx = total - 1; + } + + flat_pixels.select_nth_unstable(idx); + + return Some(flat_pixels[idx]); + } + + // If pct == None, no cutoff on that side: + None +} + fn read_and_encode( image_bytes: &[u8], mut transform: impl FnMut(DynamicImage) -> DynamicImage, diff --git a/client/index.html b/client/index.html index 0c2f53b..e803818 100644 --- a/client/index.html +++ b/client/index.html @@ -3,11 +3,12 @@ + Rustoscope
- + diff --git a/client/package.json b/client/package.json index 6e715f6..ef5cc61 100644 --- a/client/package.json +++ b/client/package.json @@ -8,15 +8,27 @@ "dev": "vite", "build": "vite build", "preview": "vite preview", - "deploy": "vite build && gh-pages -d dist" + "typecheck": "tsc --noEmit", + "deploy": "vite build && gh-pages -d dist", + "build-css": "npx @tailwindcss/cli -i ./src/index.css -o ./public/output.css", + "build-css-watch": "npx @tailwindcss/cli -i ./src/index.css -o ./public/output.css --watch" }, "dependencies": { + "@tailwindcss/vite": "^4.1.8", "preact": "^10.26.5", "react-select": "^5.10.1" }, "devDependencies": { "@preact/preset-vite": "^2.10.1", + "@tailwindcss/postcss": "^4.1.8", + "@types/node": "^22.15.29", + "@types/react": "^19.1.6", + "@types/react-dom": "^19.1.6", + "autoprefixer": "^10.4.21", "gh-pages": "^6.3.0", + "postcss": "^8.5.4", + "tailwindcss": "^3.4.17", + "typescript": "^5.8.3", "vite": "^6.3.5", "wasm-pack": "^0.13.1" } diff --git a/client/pnpm-lock.yaml b/client/pnpm-lock.yaml index 777341c..fa1ae23 100644 --- a/client/pnpm-lock.yaml +++ b/client/pnpm-lock.yaml @@ -8,28 +8,59 @@ importers: .: dependencies: + '@tailwindcss/vite': + specifier: ^4.1.8 + version: 4.1.8(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)) preact: specifier: ^10.26.5 version: 10.26.6 react-select: specifier: ^5.10.1 - version: 5.10.1(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 5.10.1(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) devDependencies: '@preact/preset-vite': specifier: ^2.10.1 - version: 2.10.1(@babel/core@7.27.1)(preact@10.26.6)(vite@6.3.5) + version: 2.10.1(@babel/core@7.27.1)(preact@10.26.6)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)) + '@tailwindcss/postcss': + specifier: ^4.1.8 + version: 4.1.8 + '@types/node': + specifier: ^22.15.29 + version: 22.15.29 + '@types/react': + specifier: ^19.1.6 + version: 19.1.6 + '@types/react-dom': + specifier: ^19.1.6 + version: 19.1.6(@types/react@19.1.6) + autoprefixer: + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.4) gh-pages: specifier: ^6.3.0 version: 6.3.0 + postcss: + specifier: ^8.5.4 + version: 8.5.4 + tailwindcss: + specifier: ^3.4.17 + version: 3.4.17 + typescript: + specifier: ^5.8.3 + version: 5.8.3 vite: specifier: ^6.3.5 - version: 6.3.5 + version: 6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) wasm-pack: specifier: ^0.13.1 version: 0.13.1 packages: + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -327,6 +358,14 @@ packages: '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -357,6 +396,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@preact/preset-vite@2.10.1': resolution: {integrity: sha512-59lyGBXNfZIr5OOuBUB4/IB8AqF/ULbvYnyItgK/2BJnsGJqaeaJobRVtMp1129obHQuj8oZ/dVxB9inmH8Xig==} peerDependencies: @@ -484,19 +527,146 @@ packages: cpu: [x64] os: [win32] + '@tailwindcss/node@4.1.8': + resolution: {integrity: sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q==} + + '@tailwindcss/oxide-android-arm64@4.1.8': + resolution: {integrity: sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.8': + resolution: {integrity: sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.8': + resolution: {integrity: sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.8': + resolution: {integrity: sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.8': + resolution: {integrity: sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.8': + resolution: {integrity: sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.8': + resolution: {integrity: sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.8': + resolution: {integrity: sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.8': + resolution: {integrity: sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.8': + resolution: {integrity: sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.8': + resolution: {integrity: sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.8': + resolution: {integrity: sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.8': + resolution: {integrity: sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.1.8': + resolution: {integrity: sha512-vB/vlf7rIky+w94aWMw34bWW1ka6g6C3xIOdICKX2GC0VcLtL6fhlLiafF0DVIwa9V6EHz8kbWMkS2s2QvvNlw==} + + '@tailwindcss/vite@4.1.8': + resolution: {integrity: sha512-CQ+I8yxNV5/6uGaJjiuymgw0kEQiNKRinYbZXPdx1fk5WgiyReG0VaUx/Xq6aVNSUNJFzxm6o8FNKS5aMaim5A==} + peerDependencies: + vite: ^5.2.0 || ^6 + '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/node@22.15.29': + resolution: {integrity: sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==} + '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/react-dom@19.1.6': + resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==} + peerDependencies: + '@types/react': ^19.0.0 + '@types/react-transition-group@4.4.12': resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} peerDependencies: '@types/react': '*' - '@types/react@19.1.3': - resolution: {integrity: sha512-dLWQ+Z0CkIvK1J8+wrDPwGxEYFA4RAyHoZPxHVGspYmFVnwGSNT24cGIhFJrtfRnWVuW8X7NO52gCXmhkVUWGQ==} + '@types/react@19.1.6': + resolution: {integrity: sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + 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==} array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} @@ -505,6 +675,13 @@ packages: async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + axios@0.26.1: resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} @@ -520,6 +697,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + binary-install@1.1.0: resolution: {integrity: sha512-rkwNGW+3aQVSZoD0/o3mfPN6Yxh3Id0R/xzTVBVVpGNlVz8EGwusksxRlbk/A5iKTZt9zkMn3qIqmAt3vpfbzg==} engines: {node: '>=10'} @@ -530,6 +711,9 @@ packages: brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -543,17 +727,40 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + caniuse-lite@1.0.30001717: resolution: {integrity: sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + 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@13.1.0: resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} engines: {node: '>=18'} + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} @@ -570,6 +777,10 @@ packages: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + css-select@5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} @@ -577,6 +788,11 @@ packages: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -589,10 +805,20 @@ packages: supports-color: optional: true + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} @@ -609,12 +835,25 @@ packages: domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + electron-to-chromium@1.5.150: resolution: {integrity: sha512-rOOkP2ZUMx1yL4fCxXQKDHQ8ZXwisb2OycOQVKHgvB3ZI4CvehOd4y2tfnnLDieJ3Zs1RL1Dlp3cMkyIn7nnXA==} email-addresses@5.0.0: resolution: {integrity: sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -689,6 +928,13 @@ packages: debug: optional: true + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fs-extra@11.3.0: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} @@ -721,6 +967,14 @@ packages: 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'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -765,6 +1019,10 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + 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'} @@ -773,6 +1031,10 @@ packages: 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'} @@ -781,6 +1043,20 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -803,6 +1079,74 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + + 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==} @@ -814,6 +1158,9 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -838,6 +1185,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} @@ -846,18 +1197,34 @@ packages: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + minizlib@2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -869,6 +1236,14 @@ packages: node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -876,6 +1251,10 @@ packages: 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'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -891,6 +1270,9 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -907,9 +1289,17 @@ packages: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -925,12 +1315,57 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + 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.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + 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.4: + resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==} engines: {node: ^10 || ^12 || >=14} preact@10.26.6: @@ -966,6 +1401,13 @@ packages: resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} 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'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -999,6 +1441,18 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + simple-code-frame@1.3.0: resolution: {integrity: sha512-MB4pQmETUBlNs62BBeRjIFGeuy/x6gGKh7+eRUemn1rCFhqo7K+4slPqsyizCbcbYLnaYqaoZ2FWsZ/jN06D8w==} @@ -1022,6 +1476,22 @@ packages: resolution: {integrity: sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==} engines: {node: '>=16'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + strip-outer@1.0.1: resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==} engines: {node: '>=0.10.0'} @@ -1029,14 +1499,42 @@ packages: stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + 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.17: + resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + engines: {node: '>=14.0.0'} + hasBin: true + + tailwindcss@4.1.8: + resolution: {integrity: sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og==} + + tapable@2.2.2: + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + engines: {node: '>=6'} + tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + + 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==} + tinyglobby@0.2.13: resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} engines: {node: '>=12.0.0'} @@ -1049,6 +1547,17 @@ packages: resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==} engines: {node: '>=0.10.0'} + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -1068,6 +1577,9 @@ packages: '@types/react': optional: true + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + vite-prerender-plugin@0.5.10: resolution: {integrity: sha512-m4i0G5oc3LPLA02uW2XsFZmYNxZdyryz5Ksi78O9puj/ao5c8dBUW06caGwoM1TmYknTBBUyKhtqajUpoP+z8Q==} peerDependencies: @@ -1117,6 +1629,19 @@ packages: resolution: {integrity: sha512-P9exD4YkjpDbw68xUhF3MDm/CC/3eTmmthyG5bHJ56kalxOTewOunxTke4SyF8MTXV6jUtNjXggPgrGmMtczGg==} hasBin: true + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -1126,12 +1651,23 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + snapshots: + '@alloc/quick-lru@5.2.0': {} + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.8 @@ -1294,7 +1830,7 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.1.3)(react@19.1.0)': + '@emotion/react@11.14.0(@types/react@19.1.6)(react@19.1.0)': dependencies: '@babel/runtime': 7.27.1 '@emotion/babel-plugin': 11.13.5 @@ -1306,7 +1842,7 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.6 transitivePeerDependencies: - supports-color @@ -1416,6 +1952,19 @@ snapshots: '@floating-ui/utils@0.2.9': {} + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 @@ -1445,18 +1994,21 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@preact/preset-vite@2.10.1(@babel/core@7.27.1)(preact@10.26.6)(vite@6.3.5)': + '@pkgjs/parseargs@0.11.0': + optional: true + + '@preact/preset-vite@2.10.1(@babel/core@7.27.1)(preact@10.26.6)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))': dependencies: '@babel/core': 7.27.1 '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.27.1) - '@prefresh/vite': 2.4.7(preact@10.26.6)(vite@6.3.5) + '@prefresh/vite': 2.4.7(preact@10.26.6)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)) '@rollup/pluginutils': 4.2.1 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.27.1) debug: 4.4.0 kolorist: 1.8.0 - vite: 6.3.5 - vite-prerender-plugin: 0.5.10(vite@6.3.5) + vite: 6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) + vite-prerender-plugin: 0.5.10(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)) transitivePeerDependencies: - preact - supports-color @@ -1469,7 +2021,7 @@ snapshots: '@prefresh/utils@1.2.0': {} - '@prefresh/vite@2.4.7(preact@10.26.6)(vite@6.3.5)': + '@prefresh/vite@2.4.7(preact@10.26.6)(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))': dependencies: '@babel/core': 7.27.1 '@prefresh/babel-plugin': 0.5.1 @@ -1477,7 +2029,7 @@ snapshots: '@prefresh/utils': 1.2.0 '@rollup/pluginutils': 4.2.1 preact: 10.26.6 - vite: 6.3.5 + vite: 6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) transitivePeerDependencies: - supports-color @@ -1546,22 +2098,138 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.40.2': optional: true + '@tailwindcss/node@4.1.8': + dependencies: + '@ampproject/remapping': 2.3.0 + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + lightningcss: 1.30.1 + magic-string: 0.30.17 + source-map-js: 1.2.1 + tailwindcss: 4.1.8 + + '@tailwindcss/oxide-android-arm64@4.1.8': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.8': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.8': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.8': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.8': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.8': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.8': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.8': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.8': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.8': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.8': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.8': + optional: true + + '@tailwindcss/oxide@4.1.8': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.8 + '@tailwindcss/oxide-darwin-arm64': 4.1.8 + '@tailwindcss/oxide-darwin-x64': 4.1.8 + '@tailwindcss/oxide-freebsd-x64': 4.1.8 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.8 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.8 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.8 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.8 + '@tailwindcss/oxide-linux-x64-musl': 4.1.8 + '@tailwindcss/oxide-wasm32-wasi': 4.1.8 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.8 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.8 + + '@tailwindcss/postcss@4.1.8': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.1.8 + '@tailwindcss/oxide': 4.1.8 + postcss: 8.5.4 + tailwindcss: 4.1.8 + + '@tailwindcss/vite@4.1.8(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))': + dependencies: + '@tailwindcss/node': 4.1.8 + '@tailwindcss/oxide': 4.1.8 + tailwindcss: 4.1.8 + vite: 6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) + '@types/estree@1.0.7': {} + '@types/node@22.15.29': + dependencies: + undici-types: 6.21.0 + '@types/parse-json@4.0.2': {} - '@types/react-transition-group@4.4.12(@types/react@19.1.3)': + '@types/react-dom@19.1.6(@types/react@19.1.6)': dependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.6 - '@types/react@19.1.3': + '@types/react-transition-group@4.4.12(@types/react@19.1.6)': + dependencies: + '@types/react': 19.1.6 + + '@types/react@19.1.6': dependencies: csstype: 3.1.3 + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + array-union@2.1.0: {} async@3.2.6: {} + autoprefixer@10.4.21(postcss@8.5.4): + dependencies: + browserslist: 4.24.5 + caniuse-lite: 1.0.30001717 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.4 + postcss-value-parser: 4.2.0 + axios@0.26.1: dependencies: follow-redirects: 1.15.9 @@ -1580,6 +2248,8 @@ snapshots: balanced-match@1.0.2: {} + binary-extensions@2.3.0: {} + binary-install@1.1.0: dependencies: axios: 0.26.1 @@ -1595,6 +2265,10 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -1608,12 +2282,36 @@ snapshots: callsites@3.1.0: {} + camelcase-css@2.0.1: {} + caniuse-lite@1.0.30001717: {} + 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 + chownr@2.0.0: {} + chownr@3.0.0: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + commander@13.1.0: {} + commander@4.1.1: {} + commondir@1.0.1: {} concat-map@0.0.1: {} @@ -1630,6 +2328,12 @@ snapshots: path-type: 4.0.0 yaml: 1.10.2 + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + css-select@5.1.0: dependencies: boolbase: 1.0.0 @@ -1640,16 +2344,24 @@ snapshots: css-what@6.1.0: {} + cssesc@3.0.0: {} + csstype@3.1.3: {} debug@4.4.0: dependencies: ms: 2.1.3 + detect-libc@2.0.4: {} + + didyoumean@1.2.2: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 + dlv@1.1.3: {} + dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.27.1 @@ -1673,10 +2385,21 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + eastasianwidth@0.2.0: {} + electron-to-chromium@1.5.150: {} email-addresses@5.0.0: {} + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.2 + entities@4.5.0: {} error-ex@1.3.2: @@ -1762,6 +2485,13 @@ snapshots: follow-redirects@1.15.9: {} + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fraction.js@4.3.7: {} + fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 @@ -1795,6 +2525,19 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -1843,18 +2586,36 @@ snapshots: is-arrayish@0.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: {} + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.7: {} + + jiti@2.4.2: {} + js-tokens@4.0.0: {} jsesc@3.1.0: {} @@ -1871,6 +2632,53 @@ snapshots: kolorist@1.8.0: {} + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 + + lilconfig@3.1.3: {} + lines-and-columns@1.2.4: {} locate-path@5.0.0: @@ -1881,6 +2689,8 @@ snapshots: dependencies: js-tokens: 4.0.0 + lru-cache@10.4.3: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -1906,21 +2716,39 @@ snapshots: dependencies: brace-expansion: 1.1.11 + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + minipass@3.3.6: dependencies: yallist: 4.0.0 minipass@5.0.0: {} + minipass@7.1.2: {} + minizlib@2.1.2: dependencies: minipass: 3.3.6 yallist: 4.0.0 + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + mkdirp@1.0.4: {} + mkdirp@3.0.1: {} + ms@2.1.3: {} + 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-html-parser@6.1.13: @@ -1930,12 +2758,18 @@ snapshots: node-releases@2.0.19: {} + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + nth-check@2.1.1: dependencies: boolbase: 1.0.0 object-assign@4.1.1: {} + object-hash@3.0.0: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -1950,6 +2784,8 @@ snapshots: p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -1965,8 +2801,15 @@ snapshots: path-is-absolute@1.0.1: {} + path-key@3.1.1: {} + path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + path-type@4.0.0: {} picocolors@1.1.1: {} @@ -1975,11 +2818,46 @@ snapshots: picomatch@4.0.2: {} + pify@2.3.0: {} + + pirates@4.0.7: {} + pkg-dir@4.2.0: dependencies: find-up: 4.1.0 - postcss@8.5.3: + postcss-import@15.1.0(postcss@8.5.4): + dependencies: + postcss: 8.5.4 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.10 + + postcss-js@4.0.1(postcss@8.5.4): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.4 + + postcss-load-config@4.0.2(postcss@8.5.4): + dependencies: + lilconfig: 3.1.3 + yaml: 2.8.0 + optionalDependencies: + postcss: 8.5.4 + + postcss-nested@6.2.0(postcss@8.5.4): + dependencies: + postcss: 8.5.4 + 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.4: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 @@ -2002,19 +2880,19 @@ snapshots: react-is@16.13.1: {} - react-select@5.10.1(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-select@5.10.1(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.27.1 '@emotion/cache': 11.14.0 - '@emotion/react': 11.14.0(@types/react@19.1.3)(react@19.1.0) + '@emotion/react': 11.14.0(@types/react@19.1.6)(react@19.1.0) '@floating-ui/dom': 1.7.0 - '@types/react-transition-group': 4.4.12(@types/react@19.1.3) + '@types/react-transition-group': 4.4.12(@types/react@19.1.6) memoize-one: 6.0.0 prop-types: 15.8.1 react: 19.1.0 react-dom: 19.1.0(react@19.1.0) react-transition-group: 4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - use-isomorphic-layout-effect: 1.2.0(@types/react@19.1.3)(react@19.1.0) + use-isomorphic-layout-effect: 1.2.0(@types/react@19.1.6)(react@19.1.0) transitivePeerDependencies: - '@types/react' - supports-color @@ -2030,6 +2908,14 @@ snapshots: react@19.1.0: {} + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + resolve-from@4.0.0: {} resolve@1.22.10: @@ -2078,6 +2964,14 @@ snapshots: semver@6.3.1: {} + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@4.1.0: {} + simple-code-frame@1.3.0: dependencies: kolorist: 1.8.0 @@ -2092,14 +2986,75 @@ snapshots: stack-trace@1.0.0-pre2: {} + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + strip-outer@1.0.1: dependencies: escape-string-regexp: 1.0.5 stylis@4.2.0: {} + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + ts-interface-checker: 0.1.13 + supports-preserve-symlinks-flag@1.0.0: {} + tailwindcss@3.4.17: + 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.4 + postcss-import: 15.1.0(postcss@8.5.4) + postcss-js: 4.0.1(postcss@8.5.4) + postcss-load-config: 4.0.2(postcss@8.5.4) + postcss-nested: 6.2.0(postcss@8.5.4) + postcss-selector-parser: 6.1.2 + resolve: 1.22.10 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + tailwindcss@4.1.8: {} + + tapable@2.2.2: {} + tar@6.2.1: dependencies: chownr: 2.0.0 @@ -2109,6 +3064,23 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + tinyglobby@0.2.13: dependencies: fdir: 6.4.4(picomatch@4.0.2) @@ -2122,6 +3094,12 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 + ts-interface-checker@0.1.13: {} + + typescript@5.8.3: {} + + undici-types@6.21.0: {} + universalify@2.0.1: {} update-browserslist-db@1.1.3(browserslist@4.24.5): @@ -2130,13 +3108,15 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - use-isomorphic-layout-effect@1.2.0(@types/react@19.1.3)(react@19.1.0): + use-isomorphic-layout-effect@1.2.0(@types/react@19.1.6)(react@19.1.0): dependencies: react: 19.1.0 optionalDependencies: - '@types/react': 19.1.3 + '@types/react': 19.1.6 + + util-deprecate@1.0.2: {} - vite-prerender-plugin@0.5.10(vite@6.3.5): + vite-prerender-plugin@0.5.10(vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)): dependencies: kolorist: 1.8.0 magic-string: 0.30.17 @@ -2144,18 +3124,22 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.4 stack-trace: 1.0.0-pre2 - vite: 6.3.5 + vite: 6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) - vite@6.3.5: + vite@6.3.5(@types/node@22.15.29)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0): dependencies: esbuild: 0.25.4 fdir: 6.4.4(picomatch@4.0.2) picomatch: 4.0.2 - postcss: 8.5.3 + postcss: 8.5.4 rollup: 4.40.2 tinyglobby: 0.2.13 optionalDependencies: + '@types/node': 22.15.29 fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.30.1 + yaml: 2.8.0 wasm-pack@0.13.1: dependencies: @@ -2163,10 +3147,30 @@ snapshots: transitivePeerDependencies: - debug + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + wrappy@1.0.2: {} yallist@3.1.1: {} yallist@4.0.0: {} + yallist@5.0.0: {} + yaml@1.10.2: {} + + yaml@2.8.0: {} diff --git a/client/postcss.config.js b/client/postcss.config.js new file mode 100644 index 0000000..a27b56b --- /dev/null +++ b/client/postcss.config.js @@ -0,0 +1,7 @@ +// postcss.config.js +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; \ No newline at end of file diff --git a/client/public/output.css b/client/public/output.css new file mode 100644 index 0000000..6da6dda --- /dev/null +++ b/client/public/output.css @@ -0,0 +1,169 @@ +/*! tailwindcss v4.1.8 | MIT License | https://tailwindcss.com */ +@layer properties; +.block { + display: block; +} +.flex { + display: flex; +} +.hidden { + display: none; +} +.h-full { + height: 100%; +} +.h-screen { + height: 100vh; +} +.w-1\/5 { + width: calc(1/5 * 100%); +} +.w-2\/5 { + width: calc(2/5 * 100%); +} +.w-\[150px\] { + width: 150px; +} +.w-full { + width: 100%; +} +.flex-grow { + flex-grow: 1; +} +.cursor-not-allowed { + cursor: not-allowed; +} +.cursor-pointer { + cursor: pointer; +} +.flex-col { + flex-direction: column; +} +.flex-row { + flex-direction: row; +} +.items-center { + align-items: center; +} +.items-start { + align-items: flex-start; +} +.justify-center { + justify-content: center; +} +.justify-start { + justify-content: flex-start; +} +.border { + border-style: var(--tw-border-style); + border-width: 1px; +} +.border-2 { + border-style: var(--tw-border-style); + border-width: 2px; +} +.border-dashed { + --tw-border-style: dashed; + border-style: dashed; +} +.object-contain { + object-fit: contain; +} +.text-center { + text-align: center; +} +.opacity-50 { + opacity: 50%; +} +.grayscale { + --tw-grayscale: grayscale(100%); + filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); +} +.invert { + --tw-invert: invert(100%); + filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); +} +.focus\:outline-none { + &:focus { + --tw-outline-style: none; + outline-style: none; + } +} +@property --tw-border-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} +@property --tw-blur { + syntax: "*"; + inherits: false; +} +@property --tw-brightness { + syntax: "*"; + inherits: false; +} +@property --tw-contrast { + syntax: "*"; + inherits: false; +} +@property --tw-grayscale { + syntax: "*"; + inherits: false; +} +@property --tw-hue-rotate { + syntax: "*"; + inherits: false; +} +@property --tw-invert { + syntax: "*"; + inherits: false; +} +@property --tw-opacity { + syntax: "*"; + inherits: false; +} +@property --tw-saturate { + syntax: "*"; + inherits: false; +} +@property --tw-sepia { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-drop-shadow-size { + syntax: "*"; + inherits: false; +} +@layer properties { + @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { + *, ::before, ::after, ::backdrop { + --tw-border-style: solid; + --tw-blur: initial; + --tw-brightness: initial; + --tw-contrast: initial; + --tw-grayscale: initial; + --tw-hue-rotate: initial; + --tw-invert: initial; + --tw-opacity: initial; + --tw-saturate: initial; + --tw-sepia: initial; + --tw-drop-shadow: initial; + --tw-drop-shadow-color: initial; + --tw-drop-shadow-alpha: 100%; + --tw-drop-shadow-size: initial; + } + } +} diff --git a/client/src/app.css b/client/src/app.css deleted file mode 100644 index 16cb8af..0000000 --- a/client/src/app.css +++ /dev/null @@ -1,7 +0,0 @@ -body { - font-family: system-ui, sans-serif; - background: #f8f9fa; - margin: 0; - padding: 2rem; - color: #333; -} diff --git a/client/src/app.jsx b/client/src/app.jsx deleted file mode 100644 index f244a39..0000000 --- a/client/src/app.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import "./app.css"; -import ImageConverter from "./images/ImageConverter.jsx"; -import { useWasm } from "./useWasm.js"; - -export function App() { - const { wasmReady, wasmError } = useWasm(); - console.log("WASM ready:", wasmReady); - - return ( -
- {wasmError &&

Error initializing WASM: {wasmError.message}

} - -
- ); -} diff --git a/client/src/app.tsx b/client/src/app.tsx new file mode 100644 index 0000000..85c3ef5 --- /dev/null +++ b/client/src/app.tsx @@ -0,0 +1,17 @@ +import ImageConverter from "@/components/images/ImageConverter.jsx"; +import { useWasm } from "./hooks/useWasm.js"; + +export function App() { + const { wasmReady, wasmError } = useWasm(); + console.log("WASM ready:", wasmReady); + + return ( +
+ {wasmError &&

Error initializing WASM: {wasmError.message}

} +
+
Rustoscope
+
+ +
+ ); +} diff --git a/client/src/assets/preact.svg b/client/src/assets/preact.svg deleted file mode 100644 index 908f17d..0000000 --- a/client/src/assets/preact.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/src/components/images/ImageConverter.jsx b/client/src/components/images/ImageConverter.jsx new file mode 100644 index 0000000..fd3feb7 --- /dev/null +++ b/client/src/components/images/ImageConverter.jsx @@ -0,0 +1,220 @@ +import { useState } from "preact/hooks"; +import Select from "react-select"; +import { useWasm } from "@/hooks/useWasm.ts"; +import { to_grayscale, invert_colors, to_png, clip_pixels_with_percentiles } from "@/wasm/wasm_api.js"; +import ImagePreview from "./ImagePreview.jsx"; + +const options = [ + { value: "grayscale", label: "Grayscale" }, + { value: "invert", label: "Invert" }, +]; + +const convert = (rawBytes, conversionType) => { + switch (conversionType) { + case "grayscale": + return to_grayscale(rawBytes); + case "invert": + return invert_colors(rawBytes); + default: + throw new Error("Unsupported conversion type"); + } +} + +const processBytes = (fileType, bytes) => { + switch (fileType) { + case "image/png": + return bytes; + case "image/jpg": + return bytes; + case "image/tiff": + return to_png(bytes); + default: + throw new Error("Unsupported image type. Supported: [png, jpg, tiff]"); + } +} + +const ImageConverter = () => { + const { wasmReady } = useWasm(); + + const [imgSrc, setImgSrc] = useState(null); + const [imgResult, setImgResult] = useState(null); + const [rawBytes, setRawBytes] = useState(null); + const [previesAspectRatios, setPreviesAspectRatios] = useState(16 / 10); + const [conversionType, setConversionType] = useState(null); + const [errorMessage, setErrorMessage] = useState(null); + const [lowPercentile, setLowPercentile] = useState(5); + const [highPercentile, setHighPercentile] = useState(95); + const [removeHotPixels, setRemoveHotPixels] = useState(false); + + const handleUpload = async (e) => { + const file = e.target.files?.[0]; + if (!file) return; + + const arrayBuffer = await file.arrayBuffer(); + const bytes = new Uint8Array(arrayBuffer); + + setErrorMessage(null); + + try { + const processedBytes = processBytes(file.type, bytes); + setRawBytes(processedBytes); + const blob = new Blob([processedBytes]); + setImgSrc(URL.createObjectURL(blob)); + } catch (err) { + console.error(`Upload error: ${err}`) + setErrorMessage(`Upload error: ${err}`) + setImgSrc(null); + setRawBytes(null); + } + }; + + const handleConvert = async () => { + if (!rawBytes || !wasmReady) return; + + setErrorMessage(null); + + try { + const convertedBytes = convert(rawBytes, conversionType.value); + + let finalBytes = convertedBytes; + if (removeHotPixels) { + try { + finalBytes = clip_pixels_with_percentiles( + convertedBytes, + lowPercentile, + highPercentile + ); + } catch (hpErr) { + console.error(`Hot-pixel removal error: ${hpErr}`); + setErrorMessage(`Hot-pixel removal error: ${hpErr}`); + finalBytes = convertedBytes; + } + } + + const blob = new Blob([finalBytes], { type: "image/png" }); + setImgResult(URL.createObjectURL(blob)); + } catch (convErr) { + console.error(`Conversion error: ${convErr}`); + setErrorMessage(`Conversion error: ${convErr}`); + } + }; + + return ( +
+
+ +
+
+ +
+
+
Options
+
+
+ + + +
+ + setRemoveHotPixels(e.currentTarget.checked)} + className='form-checkbox h-4 w-4 text-blue-600 rounded mr-2 focus:ring-blue-500' + /> + Remove hot-pixels + +
+ + { + const val = e.currentTarget.valueAsNumber; + if (!Number.isNaN(val) && val >= 0 && val <= 100) { + setLowPercentile(val); + } + }} + className='w-24 px-3 py-1 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 text-sm' + /> +
+
+ + { + const val = e.currentTarget.valueAsNumber; + if (!Number.isNaN(val) && val >= 0 && val <= 100) { + setHighPercentile(val); + } + }} + className='w-24 px-3 py-1 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 text-sm' + /> +
+
+
+
+ ); +}; + +export default ImageConverter; diff --git a/client/src/images/ImagePreview.jsx b/client/src/components/images/ImagePreview.jsx similarity index 60% rename from client/src/images/ImagePreview.jsx rename to client/src/components/images/ImagePreview.jsx index 5f14e19..7145a12 100644 --- a/client/src/images/ImagePreview.jsx +++ b/client/src/components/images/ImagePreview.jsx @@ -1,5 +1,4 @@ import { useEffect, useRef } from "preact/hooks"; -import "./ImagePreview.css"; const ImagePreview = ({ imageUrl, @@ -34,16 +33,16 @@ const ImagePreview = ({ }; return ( -
- {header &&

{header}

} +
+ {header &&
{header}
} {imageUrl ? ( - Preview + Preview ) : ( -
-

{emptyText || "No image available"}

+
+

{emptyText || "No image available"}

)} - {error && {error}} + {error && {error}}
); }; diff --git a/client/src/useWasm.js b/client/src/hooks/useWasm.ts similarity index 60% rename from client/src/useWasm.js rename to client/src/hooks/useWasm.ts index 6c457de..47c6a18 100644 --- a/client/src/useWasm.js +++ b/client/src/hooks/useWasm.ts @@ -1,16 +1,16 @@ import { useEffect, useState } from "preact/hooks"; -import init from "./wasm/wasm_api.js"; +import init from "../wasm/wasm_api.js"; export function useWasm() { - const [wasmReady, setWasmReady] = useState(false); - const [wasmError, setWasmError] = useState(null); + const [wasmReady, setWasmReady] = useState(false); + const [wasmError, setWasmError] = useState(null); useEffect(() => { const loadWasm = async () => { try { await init(); setWasmReady(true); - } catch (error) { + } catch (error: any) { setWasmError(error); } }; diff --git a/client/src/images/ImageConverter.css b/client/src/images/ImageConverter.css deleted file mode 100644 index a162e7c..0000000 --- a/client/src/images/ImageConverter.css +++ /dev/null @@ -1,99 +0,0 @@ -.image-converter { - max-width: 100%; - margin: 0 auto; - padding: 2rem; - background: white; - border-radius: 1rem; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); -} - -.image-converter-header { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - align-items: center; - gap: 1rem; - margin-bottom: 2rem; -} - -.image-converter-header h1 { - font-size: 2rem; - flex: 1 1 auto; - margin: 0; - color: #333; -} - -.image-converter-controls { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 0.75rem; -} - -.image-converter-controls input[type="file"] { - display: none; -} - -.image-converter-controls button { - padding: 0.5rem 1.25rem; - font-size: 0.95rem; - background-color: #007bff; - border: none; - border-radius: 6px; - color: white; - cursor: pointer; - transition: background-color 0.3s ease; -} - -.image-converter-controls button:hover:not(:disabled) { - background-color: #0056b3; -} - -.image-converter-controls button:disabled { - background-color: #ccc; - cursor: not-allowed; -} - -.conversion-select { - padding: 0.5rem 1.25rem; - font-size: 0.95rem; - border-radius: 6px; - border: 1px solid #ccc; - background-color: white; - color: #333; - cursor: pointer; - transition: border-color 0.3s ease, box-shadow 0.3s ease; -} - -.conversion-select:focus { - outline: none; - border-color: #007bff; - box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); -} - -.conversion-select:hover { - border-color: #888; -} - -.image-preview-container { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: 1.5rem; - margin-top: 2rem; - margin-bottom: 2rem; -} - -.upload-label { - display: inline-block; - padding: 0.5rem 1.25rem; - background-color: #28a745; - color: white; - border-radius: 6px; - font-size: 0.95rem; - cursor: pointer; - transition: background-color 0.3s ease; -} - -.upload-label:hover { - background-color: #218838; -} diff --git a/client/src/images/ImageConverter.jsx b/client/src/images/ImageConverter.jsx deleted file mode 100644 index 4b1453f..0000000 --- a/client/src/images/ImageConverter.jsx +++ /dev/null @@ -1,144 +0,0 @@ -import "./ImageConverter.css"; -import { useState } from "preact/hooks"; -import Select from "react-select"; -import { useWasm } from "../useWasm.js"; -import { to_grayscale, invert_colors, to_png } from "../wasm/wasm_api.js"; -import ImagePreview from "./ImagePreview.jsx"; - -const options = [ - { value: "grayscale", label: "Grayscale" }, - { value: "invert", label: "Invert" }, -]; - -const convert = (rawBytes, conversionType) => { - switch (conversionType) { - case "grayscale": - return to_grayscale(rawBytes); - case "invert": - return invert_colors(rawBytes); - default: - throw new Error("Unsupported conversion type"); - } -} - -const processBytes = (fileType, bytes) => { - switch (fileType) { - case "image/png": - return bytes; - case "image/jpg": - return bytes; - case "image/tiff": - return to_png(bytes); - default: - throw new Error("Unsupported image type. Supported: [png, jpg, tiff]"); - } -} - -const ImageConverter = () => { - const { wasmReady } = useWasm(); - - const [imgSrc, setImgSrc] = useState(null); - const [imgResult, setImgResult] = useState(null); - const [rawBytes, setRawBytes] = useState(null); - const [previesAspectRatios, setPreviesAspectRatios] = useState(16 / 10); - const [conversionType, setConversionType] = useState(null); - const [errorMessage, setErrorMessage] = useState(null); - - const handleUpload = async (e) => { - const file = e.target.files?.[0]; - if (!file) return; - - const arrayBuffer = await file.arrayBuffer(); - const bytes = new Uint8Array(arrayBuffer); - - setErrorMessage(null); - - try { - const processedBytes = processBytes(file.type, bytes); - setRawBytes(processedBytes); - const blob = new Blob([processedBytes]); - setImgSrc(URL.createObjectURL(blob)); - } catch (err) { - console.error(`Upload error: ${err}`) - setErrorMessage(`Upload error: ${err}`) - setImgSrc(null); - setRawBytes(null); - } - }; - - const handleConvert = async () => { - if (!rawBytes || !wasmReady) return; - - const output = convert(rawBytes, conversionType.value); - const blob = new Blob([output], { type: "image/png" }); - setImgResult(URL.createObjectURL(blob)); - }; - - return ( -
-
-

Image Converter

-
- - -
-
-
- - -
-
- ); -}; - -export default ImageConverter; diff --git a/client/src/images/ImagePreview.css b/client/src/images/ImagePreview.css deleted file mode 100644 index 90f9ccd..0000000 --- a/client/src/images/ImagePreview.css +++ /dev/null @@ -1,34 +0,0 @@ -.image-preview { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; -} - -.image-preview h2 { - margin-bottom: 1rem; - font-size: 1rem; - text-align: center; -} - -.image-preview img, -.empty-preview { - width: 100%; - object-fit: contain; - border-radius: 0.5rem; - background-color: #f9f9f9; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); -} - -.empty-preview { - display: flex; - justify-content: center; - align-items: center; - border: 2px dashed #ddd; - background-color: #f9f9f9; -} - -.error-message { - margin-top: 20px; - color: red; -} diff --git a/client/src/index.css b/client/src/index.css index e69de29..bd6213e 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/client/src/main.jsx b/client/src/main.jsx deleted file mode 100644 index 26b0467..0000000 --- a/client/src/main.jsx +++ /dev/null @@ -1,5 +0,0 @@ -import { render } from 'preact' -import './index.css' -import { App } from './app.jsx' - -render(, document.getElementById('app')) diff --git a/client/src/main.tsx b/client/src/main.tsx new file mode 100644 index 0000000..57ffbfc --- /dev/null +++ b/client/src/main.tsx @@ -0,0 +1,11 @@ +import { render } from 'preact' +import './index.css' +import { App } from './app' + +const appRoot = document.getElementById('app'); + +if (appRoot) { + render(, appRoot); +} else { + console.error('Root element with ID "app" not found in the document.'); +} \ No newline at end of file diff --git a/client/tailwind.config.js b/client/tailwind.config.js new file mode 100644 index 0000000..6fb6968 --- /dev/null +++ b/client/tailwind.config.js @@ -0,0 +1,12 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{html,js,jsx,ts,tsx}" + ], + theme: { + extend: {}, + }, + plugins: [], +} + diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 0000000..17a35ed --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,26 @@ +// tsconfig.json +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "jsxImportSource": "preact", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src", "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.jsx"], + "exclude": ["node_modules"] +} \ No newline at end of file diff --git a/client/vite.config.js b/client/vite.config.ts similarity index 94% rename from client/vite.config.js rename to client/vite.config.ts index 8138ffb..8596b3a 100644 --- a/client/vite.config.js +++ b/client/vite.config.ts @@ -9,6 +9,7 @@ export default defineConfig({ alias: { react: "preact/compat", "react-dom": "preact/compat", + "@": "/src" }, }, build: {