diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65c253a..118168c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: - name: Set up pnpm uses: pnpm/action-setup@v5 with: - version: 11.2.2 + version: 12.10.1 - name: Set up Node uses: actions/setup-node@v5 diff --git a/CHANGELOG.md b/CHANGELOG.md index e7d4c63..142a21b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ All notable changes to **Vitrine** (formerly Photo Editor Pro) are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/). +## [1.39.0] - 2026-10-13 + +### Changed +- **A RAW keeps its shape when full quality replaces the preview.** Why: the camera corrects lens distortion in its embedded JPEG and the RAW decode did not, so on a lens with visible barrel distortion the picture widened and bowed about 10 seconds after opening. Camera match already measured that distortion (1.38.1) to pair its samples; it now applies the same radial model to the pixels, so the full-quality image has the camera's framing as well as its colour. The resample is Catmull-Rom on the 16-bit data, split across worker threads over shared memory (about 1 s for 20 MP). Photos whose preview already lines up are untouched. Note: on affected photos the frame is now cropped the way the camera crops it, so a crop or mask saved before this release sits a few percent off towards the edges. The cache key was bumped so every RAW decodes once more. Affects: `electron/cameraMatchGeometry.cjs` (new), `electron/cameraMatch.cjs`, `electron/baseCache.cjs`, `package.json` (`asarUnpack`). +- **Camera match holds contrast better in fine detail.** Why: the colour fit ran on a 200x150 grid of cell averages, and a tone curve fitted on averages reads too little contrast wherever the detail is finer than a cell. On a portrait of foliage against a bright sky the highlights came out about 20 8-bit levels darker than the camera JPEG; on a 400x300 grid that is about 12, with the four other test photos unchanged or slightly better, for roughly 0.3 s more per decode. It is still not an exact match on that kind of photo. Affects: `electron/cameraMatch.cjs`. +- Updated `exifreader` to 4.50.0 and `sharp` to 0.35.5. Why: `pnpm audit --prod` reported high-severity advisories in both (and in `@xmldom/xmldom` through exifreader); they parse the photos you open. The production audit went from 26 advisories to 8, all in `adm-zip`, which only runs in the install script of `onnxruntime-node`. +- CI installs with pnpm 12.10.1, the version the releases are built with. + +### Fixed +- **A camera-match worker that died silently left the photo stuck on "Developing full quality".** Cause: only the worker's `message` and `error` events were handled, so an exit without either never settled the promise. Fix: an `exit` handler rejects it and the open falls back to the unmatched decode. Affects: `electron/cameraMatch.cjs`. +- **A malformed log entry from the window could drop a whole batch of log lines.** Cause: the log writer called `level.toUpperCase()` on whatever it was sent, inside the batch flush. Fix: entries are validated, truncated and stripped of newlines before they are queued. Affects: `electron/main.cjs`. +- Writing metadata into a file now requires an image extension, like the other write paths. Affects: `electron/main.cjs`. + ## [1.38.1] - 2026-10-12 ### Fixed diff --git a/electron/baseCache.cjs b/electron/baseCache.cjs index 855c90b..3e0e046 100644 --- a/electron/baseCache.cjs +++ b/electron/baseCache.cjs @@ -58,7 +58,9 @@ function optionsHash(options) { // under the matched key — the bump makes them miss so the RAW decodes fresh. // cm3: the fit now registers the preview to the decode (lens-distortion-corrected JPEGs); // bases fitted without it have flattened highlights. - return crypto.createHash('sha1').update(`${demosaic}|${highlightMode}|cm3:${cameraMatch ? 1 : 0}`).digest('hex').slice(0, 8); + // cm5: matched bases are also resampled to the camera JPEG's geometry (cameraMatchGeometry.cjs) + // and fitted on the 400x300 grid (cm4 was a pre-release key for the geometry change alone). + return crypto.createHash('sha1').update(`${demosaic}|${highlightMode}|cm5:${cameraMatch ? 1 : 0}`).digest('hex').slice(0, 8); } /** diff --git a/electron/cameraMatch.cjs b/electron/cameraMatch.cjs index c9205a2..5f8681e 100644 --- a/electron/cameraMatch.cjs +++ b/electron/cameraMatch.cjs @@ -32,12 +32,17 @@ const { Worker } = require('worker_threads'); const path = require('path'); const { findEmbeddedJpegs, readOrientation } = require('./embeddedPreview.cjs'); - -// Analysis grid. 200x150 ≈ 30k cells → ~20-28k usable fit samples on a typical -// photo; each cell averages ~500 source pixels, so per-pixel detail differences -// (sharpening halos, noise reduction) wash out of the fit target. -const GW = 200; -const GH = 150; +const { undistortParallel } = require('./cameraMatchGeometry.cjs'); + +// Analysis grid. 400x300 = 120k cells; each cell averages ~170 source pixels of a +// 20MP decode, so per-pixel detail differences (sharpening halos, noise +// reduction) still wash out of the fit target. It was 200x150 until the preview +// was registered to the decode: a tone curve fitted on coarser averages reads +// too little contrast wherever the detail is finer than a cell (foliage against +// sky measured 20 8-bit levels dark in the highlights at 200x150, 12 at 400x300), +// and finer cells only pair correctly once the two images line up. +const GW = 400; +const GH = 300; // Fit hyper-parameters (validated on a 56-image corpus + the 4-photo comparison // set; N13/λ0.5 with perceptual weighting matched or improved every case over @@ -460,8 +465,22 @@ async function applyCameraMatch(decoded, orfBuf, log = console) { return decoded; } const t0 = Date.now(); - const data = await runApplyWorker(decoded.data, model); - log.log(`camera-match: fitted and applied in ${Date.now() - t0}ms (${decoded.width}x${decoded.height}, preview distortion k=${k.toFixed(4)})`); + let data = await runApplyWorker(decoded.data, model); + const tColour = Date.now() - t0; + // Give the pixels the camera's geometry too, so the picture does not change + // shape when full quality replaces the preview. Only k > 0 (barrel corrected + // by the camera): the corrected frame is then a crop of the decode. On + // failure keep the colour-matched, uncorrected decode. + let geometry = 'unchanged'; + if (k > 0) { + try { + data = await undistortParallel(data, decoded.width, decoded.height, k); + geometry = `corrected in ${Date.now() - t0 - tColour}ms`; + } catch (err) { + log.warn(`camera-match: geometry correction failed (${err.message}) — keeping the decode's geometry`); + } + } + log.log(`camera-match: fitted and applied in ${tColour}ms (${decoded.width}x${decoded.height}, preview distortion k=${k.toFixed(4)}, geometry ${geometry})`); return { ...decoded, data }; } catch (err) { log.warn(`camera-match failed (${err.message}) — using unmatched decode`); @@ -495,6 +514,8 @@ function runApplyWorker(dataArrayBuffer, model) { worker.terminate(); reject(err); }); + // A worker that dies without posting or erroring would otherwise leave the open hanging. + worker.once('exit', (code) => reject(new Error(`camera-match worker exited with code ${code}`))); worker.postMessage({ data: dataArrayBuffer }, [dataArrayBuffer]); }); } diff --git a/electron/cameraMatchGeometry.cjs b/electron/cameraMatchGeometry.cjs new file mode 100644 index 0000000..bcd682a --- /dev/null +++ b/electron/cameraMatchGeometry.cjs @@ -0,0 +1,148 @@ +// Geometry half of camera match: resample the decode into the camera JPEG's frame. +// +// cameraMatch.cjs measures the radial coefficient k that lines the embedded +// preview up with the decode (the camera bakes lens-distortion correction into +// its JPEG; the decode is the uncorrected sensor image). The colour fit only +// needs that to pair cells correctly, but leaving the decode uncorrected means +// the picture visibly changes shape when full quality replaces the preview. This +// applies the same model to the pixels, so the full-quality image has the +// camera's geometry as well as its colour. +// +// The ~20MP Catmull-Rom resample takes about 4 s on one thread, so it is split +// into row strips across worker threads over shared memory. This file is its own +// worker script, and runs from app.asar.unpacked in a packaged build (see +// package.json asarUnpack) because worker_threads cannot load from the asar. + +const { Worker, isMainThread, parentPort, workerData } = require('worker_threads'); +const os = require('os'); +const path = require('path'); + +const LUT_SIZE = 2048; +const MAX_THREADS = 8; + +/** + * cameraMatch.warpPoint maps a decode point p to the preview point q = p·(1 + k·ρp²), + * with ρ the radius normalised so the frame corner is 1. Resampling needs the + * inverse: for an output (preview-frame) radius ρq, the decode radius ρp. Returns + * ρp/ρq tabulated over ρq² in [0,1] (LUT_SIZE + 2 entries, so i+1 is always valid). + */ +function buildInverseRadialLut(k) { + const lut = new Float64Array(LUT_SIZE + 2); + for (let i = 0; i < lut.length; i++) { + const t = Math.sqrt(Math.min(1, i / LUT_SIZE)); + if (t === 0) { lut[i] = 1; continue; } + let r = t; + for (let it = 0; it < 12; it++) r -= (r + k * r * r * r - t) / (1 + 3 * k * r * r); + lut[i] = r / t; + } + return lut; +} + +/** + * Resample packed 16-bit RGB through the inverse of distortion k (Catmull-Rom). + * Only k > 0 is supported — the corrected frame is then a crop of the decode, so + * every output pixel has a source. Fills rows [yStart, yEnd) of dst (default: a + * new buffer, all rows) and returns it. + */ +function undistortRGB16(src, width, height, k, dst = new Uint16Array(src.length), yStart = 0, yEnd = height) { + const lut = buildInverseRadialLut(k); + const aspect = width / height; + const norm = (aspect * aspect + 1) / 4; + const maxX = width - 1, maxY = height - 1; + const stride = width * 3; + for (let y = yStart; y < yEnd; y++) { + const qy = (y + 0.5) / height - 0.5; + const qy2 = qy * qy; + let o = y * stride; + for (let x = 0; x < width; x++, o += 3) { + const qx = (x + 0.5) / width - 0.5; + const xa = qx * aspect; + const lt = ((xa * xa + qy2) / norm) * LUT_SIZE; + const li = lt | 0; + const s = lut[li] + (lut[li + 1] - lut[li]) * (lt - li); + const sx = (qx * s + 0.5) * width - 0.5; + const sy = (qy * s + 0.5) * height - 0.5; + const ix = Math.floor(sx), iy = Math.floor(sy); + const tx = sx - ix, ty = sy - iy; + + const wx0 = ((-tx + 2) * tx - 1) * tx * 0.5; + const wx1 = ((3 * tx - 5) * tx * tx + 2) * 0.5; + const wx2 = ((-3 * tx + 4) * tx + 1) * tx * 0.5; + const wx3 = (tx - 1) * tx * tx * 0.5; + const wy0 = ((-ty + 2) * ty - 1) * ty * 0.5; + const wy1 = ((3 * ty - 5) * ty * ty + 2) * 0.5; + const wy2 = ((-3 * ty + 4) * ty + 1) * ty * 0.5; + const wy3 = (ty - 1) * ty * ty * 0.5; + + const x0 = (ix - 1 < 0 ? 0 : ix - 1 > maxX ? maxX : ix - 1) * 3; + const x1 = (ix < 0 ? 0 : ix > maxX ? maxX : ix) * 3; + const x2 = (ix + 1 < 0 ? 0 : ix + 1 > maxX ? maxX : ix + 1) * 3; + const x3 = (ix + 2 < 0 ? 0 : ix + 2 > maxX ? maxX : ix + 2) * 3; + const r0 = (iy - 1 < 0 ? 0 : iy - 1 > maxY ? maxY : iy - 1) * stride; + const r1 = (iy < 0 ? 0 : iy > maxY ? maxY : iy) * stride; + const r2 = (iy + 1 < 0 ? 0 : iy + 1 > maxY ? maxY : iy + 1) * stride; + const r3 = (iy + 2 < 0 ? 0 : iy + 2 > maxY ? maxY : iy + 2) * stride; + + for (let c = 0; c < 3; c++) { + const v = + wy0 * (wx0 * src[r0 + x0 + c] + wx1 * src[r0 + x1 + c] + wx2 * src[r0 + x2 + c] + wx3 * src[r0 + x3 + c]) + + wy1 * (wx0 * src[r1 + x0 + c] + wx1 * src[r1 + x1 + c] + wx2 * src[r1 + x2 + c] + wx3 * src[r1 + x3 + c]) + + wy2 * (wx0 * src[r2 + x0 + c] + wx1 * src[r2 + x1 + c] + wx2 * src[r2 + x2 + c] + wx3 * src[r2 + x3 + c]) + + wy3 * (wx0 * src[r3 + x0 + c] + wx1 * src[r3 + x1 + c] + wx2 * src[r3 + x2 + c] + wx3 * src[r3 + x3 + c]); + dst[o + c] = v < 0 ? 0 : v > 65535 ? 65535 : v + 0.5 | 0; + } + } + } + return dst; +} + +/** This script on the real filesystem (same asar rule as cameraMatch.workerScriptPath). */ +function scriptPath() { + const asar = `${path.sep}app.asar${path.sep}`; + return __filename.includes(asar) ? __filename.replace(asar, `${path.sep}app.asar.unpacked${path.sep}`) : __filename; +} + +/** + * undistortRGB16 across worker threads. Takes the decode's ArrayBuffer (packed + * 16-bit RGB) and resolves with a new ArrayBuffer of the same size. + */ +function undistortParallel(dataArrayBuffer, width, height, k) { + const bytes = dataArrayBuffer.byteLength; + const srcSab = new SharedArrayBuffer(bytes); + const dstSab = new SharedArrayBuffer(bytes); + new Uint16Array(srcSab).set(new Uint16Array(dataArrayBuffer)); + const threads = Math.max(1, Math.min(MAX_THREADS, os.availableParallelism() - 1)); + const rows = Math.ceil(height / threads); + const workers = []; + const strips = []; + for (let y = 0; y < height; y += rows) { + strips.push(new Promise((resolve, reject) => { + const worker = new Worker(scriptPath(), { + workerData: { undistort: { srcSab, dstSab, width, height, k, yStart: y, yEnd: Math.min(height, y + rows) } }, + }); + workers.push(worker); + worker.once('message', (msg) => (msg && msg.ok ? resolve() : reject(new Error((msg && msg.error) || 'undistort worker failed')))); + worker.once('error', reject); + worker.once('exit', (code) => { if (code !== 0) reject(new Error(`undistort worker exited with code ${code}`)); }); + })); + } + return Promise.all(strips) + .then(() => { + const out = new Uint16Array(bytes / 2); + out.set(new Uint16Array(dstSab)); + return out.buffer; + }) + .finally(() => { for (const w of workers) w.terminate(); }); +} + +if (!isMainThread && workerData && workerData.undistort) { + try { + const { srcSab, dstSab, width, height, k, yStart, yEnd } = workerData.undistort; + undistortRGB16(new Uint16Array(srcSab), width, height, k, new Uint16Array(dstSab), yStart, yEnd); + parentPort.postMessage({ ok: true }); + } catch (err) { + parentPort.postMessage({ ok: false, error: err.message }); + } +} + +module.exports = { buildInverseRadialLut, undistortRGB16, undistortParallel, LUT_SIZE }; diff --git a/electron/main.cjs b/electron/main.cjs index 45ac4cb..929a5c6 100644 --- a/electron/main.cjs +++ b/electron/main.cjs @@ -956,7 +956,7 @@ ipcMain.handle('read-raw-metadata', async (event, filePath) => { // failure so the renderer promise rejects (no silent success). ipcMain.handle('write-image-metadata', async (event, filePath, metadata) => { try { - const safeFilePath = validateWritePath(filePath); + const safeFilePath = validateWritePath(filePath, { requireAllowedExtension: true }); markSelfWrite(safeFilePath); // in-place metadata write must not retrigger the watcher return await writeImageMetadata(safeFilePath, metadata); } catch (error) { @@ -1141,7 +1141,14 @@ const scheduleFlush = () => { ipcMain.handle('write-log', async (event, logEntry) => { try { - logQueue.push(logEntry); + // The entry comes from the renderer: a missing level would throw inside the batch + // flush and drop the whole batch, and a newline in the message would forge a log line. + if (!logEntry || typeof logEntry.level !== 'string' || typeof logEntry.message !== 'string') return false; + logQueue.push({ + timestamp: String(logEntry.timestamp), + level: logEntry.level.slice(0, 16), + message: logEntry.message.slice(0, 8192).replace(/[\r\n]+/g, ' | '), + }); scheduleFlush(); // For critical errors, flush immediately diff --git a/jest.config.cjs b/jest.config.cjs index fb778d9..93e7bf6 100644 --- a/jest.config.cjs +++ b/jest.config.cjs @@ -18,6 +18,10 @@ module.exports = { }] }, moduleNameMapper: { + // exifreader is only loaded by the main process (Node), which resolves its CommonJS + // dist build. Under jsdom Jest picks the package's "browser" export (ESM source) + // and cannot parse it, so pin the build the app really runs. + '^exifreader$': '/node_modules/exifreader/dist/exif-reader.js', '\\.(css|less|scss|sass)$': 'identity-obj-proxy', '\\.(jpg|jpeg|png|gif|webp|svg)$': '/src/__mocks__/fileMock.js', // The worker factory uses a Vite `?worker&url` import, which ts-jest cannot diff --git a/package.json b/package.json index 71d9486..caa756a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "photo_app", - "version": "1.38.1", + "version": "1.39.0", "description": "Vitrine — a free, non-destructive RAW photo editor for Windows", "main": "electron/main.cjs", "scripts": { @@ -41,14 +41,14 @@ }, "type": "module", "dependencies": { - "@img/sharp-win32-x64": "0.34.5", - "exifreader": "^4.23.5", + "@img/sharp-win32-x64": "0.35.5", + "exifreader": "^4.50.0", "libraw-wasm": "^1.1.2", "lucide-react": "^0.562.0", "onnxruntime-node": "1.27.0", "react": "^19.1.1", "react-dom": "^19.1.1", - "sharp": "0.34.5", + "sharp": "0.35.5", "zustand": "^5.0.8" }, "devDependencies": { @@ -165,6 +165,7 @@ "electron/librawWasmWorker.cjs", "electron/librawWasmNode.cjs", "electron/cameraMatchWorker.cjs", + "electron/cameraMatchGeometry.cjs", "**/node_modules/sharp/**", "**/node_modules/@img/**", "**/node_modules/onnxruntime-node/**" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0c2fd58..995a3c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,11 +9,11 @@ importers: .: dependencies: '@img/sharp-win32-x64': - specifier: 0.34.5 - version: 0.34.5 + specifier: 0.35.5 + version: 0.35.5 exifreader: - specifier: ^4.23.5 - version: 4.39.1 + specifier: ^4.50.0 + version: 4.50.0 libraw-wasm: specifier: ^1.1.2 version: 1.1.2 @@ -30,8 +30,8 @@ importers: specifier: ^19.1.1 version: 19.2.6(react@19.2.6) sharp: - specifier: 0.34.5 - version: 0.34.5 + specifier: 0.35.5 + version: 0.35.5(@types/node@25.9.1) zustand: specifier: ^5.0.8 version: 5.0.13(@types/react@19.2.15)(react@19.2.6) @@ -65,13 +65,13 @@ importers: version: 19.2.3(@types/react@19.2.15) '@typescript-eslint/eslint-plugin': specifier: ^8.44.1 - version: 8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + version: 8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^8.44.1 - version: 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + version: 8.59.4(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) '@vitejs/plugin-react': specifier: ^5.0.3 - version: 5.2.0(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)) + version: 5.2.0(supports-color@8.1.1)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)) autoprefixer: specifier: ^10.4.21 version: 10.5.0(postcss@8.5.15) @@ -80,28 +80,28 @@ importers: version: 9.2.1 electron: specifier: 43.1.0 - version: 43.1.0 + version: 43.1.0(supports-color@8.1.1) electron-builder: specifier: ^26.0.12 - version: 26.8.1(electron-builder-squirrel-windows@26.8.1) + version: 26.8.1(electron-builder-squirrel-windows@26.8.1)(supports-color@8.1.1) eslint: specifier: ^9.36.0 - version: 9.39.4(jiti@2.7.0) + version: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) eslint-plugin-react-hooks: specifier: ^7.0.1 - version: 7.1.1(eslint@9.39.4(jiti@2.7.0)) + version: 7.1.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1) eslint-plugin-react-refresh: specifier: ^0.4.21 - version: 0.4.26(eslint@9.39.4(jiti@2.7.0)) + version: 0.4.26(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) identity-obj-proxy: specifier: ^3.0.0 version: 3.0.0 jest: specifier: ^30.2.0 - version: 30.4.2(@types/node@25.9.1) + version: 30.4.2(@types/node@25.9.1)(supports-color@8.1.1) jest-environment-jsdom: specifier: ^30.2.0 - version: 30.4.1 + version: 30.4.1(supports-color@8.1.1) png-to-ico: specifier: ^3.0.1 version: 3.0.1 @@ -119,7 +119,7 @@ importers: version: 5.48.0 ts-jest: specifier: ^29.4.6 - version: 29.4.11(@babel/core@7.29.0)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.0))(jest-util@30.4.1)(jest@30.4.2(@types/node@25.9.1))(typescript@5.9.3) + version: 29.4.11(@babel/core@7.29.0(supports-color@8.1.1))(@jest/transform@30.4.1(supports-color@8.1.1))(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(esbuild@0.27.7)(jest-util@30.4.1)(jest@30.4.2(@types/node@25.9.1)(supports-color@8.1.1))(typescript@5.9.3) typescript: specifier: ^5.9.2 version: 5.9.3 @@ -128,7 +128,7 @@ importers: version: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0) wait-on: specifier: ^9.0.1 - version: 9.0.10 + version: 9.0.10(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1) packages: @@ -408,6 +408,9 @@ packages: '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} @@ -649,152 +652,161 @@ packages: resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-darwin-arm64@0.35.5': + resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-darwin-x64@0.35.5': + resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + '@img/sharp-freebsd-wasm32@0.35.5': + resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.4': + resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + '@img/sharp-libvips-darwin-x64@1.3.4': + resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + '@img/sharp-libvips-linux-arm64@1.3.4': + resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + '@img/sharp-libvips-linux-arm@1.3.4': + resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + '@img/sharp-libvips-linux-ppc64@1.3.4': + resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + '@img/sharp-libvips-linux-riscv64@1.3.4': + resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + '@img/sharp-libvips-linux-s390x@1.3.4': + resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + '@img/sharp-libvips-linux-x64@1.3.4': + resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-arm64@0.35.5': + resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-arm@0.35.5': + resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==} + engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-ppc64@0.35.5': + resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==} + engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-riscv64@0.35.5': + resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==} + engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-s390x@0.35.5': + resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==} + engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linux-x64@0.35.5': + resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linuxmusl-arm64@0.35.5': + resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-linuxmusl-x64@0.35.5': + resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-wasm32@0.35.5': + resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.5': + resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==} + engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-arm64@0.35.5': + resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==} + engines: {node: '>=20.9.0'} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-ia32@0.35.5': + resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==} + engines: {node: ^20.9.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-win32-x64@0.35.5': + resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==} + engines: {node: '>=20.9.0'} cpu: [x64] os: [win32] @@ -1522,6 +1534,10 @@ packages: resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==} engines: {node: '>=14.6'} + '@xmldom/xmldom@0.9.12': + resolution: {integrity: sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==} + engines: {node: '>=14.6'} + abbrev@4.0.0: resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} engines: {node: ^20.17.0 || >=22.9.0} @@ -2077,6 +2093,7 @@ packages: eslint@9.39.4: resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: jiti: '*' @@ -2113,8 +2130,9 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - exifreader@4.39.1: - resolution: {integrity: sha512-mQyOcAXK/mE+5rdzeqv/6tK431Vv8HCIVMYIJZJgJ+1ydd6nmg13awWcaEAzj/3M+Ii7A5v1gMokDOiK6RDcpw==} + exifreader@4.50.0: + resolution: {integrity: sha512-RUtdkMPWJd2BdinNSO0JVw0QkMigxDTsuambp+jMlPM1b5k3xvXa4p9Wat+JhW4iVBOEfpBw66PS6oYHS1yFTg==} + hasBin: true exit-x@0.2.2: resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} @@ -3269,6 +3287,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + serialize-error@7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} @@ -3277,9 +3300,14 @@ packages: resolution: {integrity: sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==} engines: {node: '>=10'} - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.35.5: + resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -3789,20 +3817,20 @@ snapshots: '@babel/compat-data@7.29.3': {} - '@babel/core@7.29.0': + '@babel/core@7.29.0(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helpers': 7.29.2 '@babel/parser': 7.29.3 '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) '@babel/types': 7.29.0 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -3827,19 +3855,19 @@ snapshots: '@babel/helper-globals@7.28.0': {} - '@babel/helper-module-imports@7.28.6': + '@babel/helper-module-imports@7.28.6(supports-color@8.1.1)': dependencies: - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-module-imports': 7.28.6(supports-color@8.1.1) '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -3860,99 +3888,99 @@ snapshots: dependencies: '@babel/types': 7.29.0 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 '@babel/runtime@7.29.2': {} @@ -3963,7 +3991,7 @@ snapshots: '@babel/parser': 7.29.3 '@babel/types': 7.29.0 - '@babel/traverse@7.29.0': + '@babel/traverse@7.29.0(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 @@ -3971,7 +3999,7 @@ snapshots: '@babel/parser': 7.29.3 '@babel/template': 7.28.6 '@babel/types': 7.29.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -4021,45 +4049,45 @@ snapshots: fs-extra: 9.1.0 minimist: 1.2.8 - '@electron/get@3.1.0': + '@electron/get@3.1.0(supports-color@8.1.1)': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) env-paths: 2.2.1 fs-extra: 8.1.0 got: 11.8.6 progress: 2.0.3 semver: 6.3.1 - sumchecker: 3.0.1 + sumchecker: 3.0.1(supports-color@8.1.1) optionalDependencies: global-agent: 3.0.0 transitivePeerDependencies: - supports-color - '@electron/get@5.0.0': + '@electron/get@5.0.0(supports-color@8.1.1)': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) env-paths: 3.0.0 graceful-fs: 4.2.11 progress: 2.0.3 semver: 7.8.1 - sumchecker: 3.0.1 + sumchecker: 3.0.1(supports-color@8.1.1) optionalDependencies: undici: 7.28.0 transitivePeerDependencies: - supports-color - '@electron/notarize@2.5.0': + '@electron/notarize@2.5.0(supports-color@8.1.1)': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 9.1.0 promise-retry: 2.0.1 transitivePeerDependencies: - supports-color - '@electron/osx-sign@1.3.3': + '@electron/osx-sign@1.3.3(supports-color@8.1.1)': dependencies: compare-version: 0.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 10.1.0 isbinaryfile: 4.0.10 minimist: 1.2.8 @@ -4067,22 +4095,22 @@ snapshots: transitivePeerDependencies: - supports-color - '@electron/rebuild@4.0.4': + '@electron/rebuild@4.0.4(supports-color@8.1.1)': dependencies: '@malept/cross-spawn-promise': 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) node-abi: 4.31.0 node-api-version: 0.2.1 node-gyp: 12.3.0 - read-binary-file-arch: 1.0.6 + read-binary-file-arch: 1.0.6(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@electron/universal@2.0.3': + '@electron/universal@2.0.3(supports-color@8.1.1)': dependencies: '@electron/asar': 3.4.1 '@malept/cross-spawn-promise': 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) dir-compare: 4.2.0 fs-extra: 11.3.5 minimatch: 9.0.9 @@ -4090,10 +4118,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@electron/windows-sign@1.2.2': + '@electron/windows-sign@1.2.2(supports-color@8.1.1)': dependencies: cross-dirname: 0.1.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 11.3.5 minimist: 1.2.8 postject: 1.0.0-alpha.6 @@ -4112,6 +4140,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 @@ -4195,17 +4228,17 @@ snapshots: '@esbuild/win32-x64@0.27.7': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0))': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))': dependencies: - eslint: 9.39.4(jiti@2.7.0) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.2': + '@eslint/config-array@0.21.2(supports-color@8.1.1)': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) minimatch: 3.1.5 transitivePeerDependencies: - supports-color @@ -4218,10 +4251,10 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.5': + '@eslint/eslintrc@3.3.5(supports-color@8.1.1)': dependencies: ajv: 6.15.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -4275,98 +4308,108 @@ snapshots: '@img/colour@1.1.0': {} - '@img/sharp-darwin-arm64@0.34.5': + '@img/sharp-darwin-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-arm64': 1.3.4 optional: true - '@img/sharp-darwin-x64@0.34.5': + '@img/sharp-darwin-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.3.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.2.4': + '@img/sharp-freebsd-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 optional: true - '@img/sharp-libvips-darwin-x64@1.2.4': + '@img/sharp-libvips-darwin-arm64@1.3.4': optional: true - '@img/sharp-libvips-linux-arm64@1.2.4': + '@img/sharp-libvips-darwin-x64@1.3.4': optional: true - '@img/sharp-libvips-linux-arm@1.2.4': + '@img/sharp-libvips-linux-arm64@1.3.4': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.4': + '@img/sharp-libvips-linux-arm@1.3.4': optional: true - '@img/sharp-libvips-linux-riscv64@1.2.4': + '@img/sharp-libvips-linux-ppc64@1.3.4': optional: true - '@img/sharp-libvips-linux-s390x@1.2.4': + '@img/sharp-libvips-linux-riscv64@1.3.4': optional: true - '@img/sharp-libvips-linux-x64@1.2.4': + '@img/sharp-libvips-linux-s390x@1.3.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + '@img/sharp-libvips-linux-x64@1.3.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.4': + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': optional: true - '@img/sharp-linux-arm64@0.34.5': + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + optional: true + + '@img/sharp-linux-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.3.4 optional: true - '@img/sharp-linux-arm@0.34.5': + '@img/sharp-linux-arm@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.3.4 optional: true - '@img/sharp-linux-ppc64@0.34.5': + '@img/sharp-linux-ppc64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.3.4 optional: true - '@img/sharp-linux-riscv64@0.34.5': + '@img/sharp-linux-riscv64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.3.4 optional: true - '@img/sharp-linux-s390x@0.34.5': + '@img/sharp-linux-s390x@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.3.4 optional: true - '@img/sharp-linux-x64@0.34.5': + '@img/sharp-linux-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.3.4 optional: true - '@img/sharp-linuxmusl-arm64@0.34.5': + '@img/sharp-linuxmusl-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 optional: true - '@img/sharp-linuxmusl-x64@0.34.5': + '@img/sharp-linuxmusl-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 optional: true - '@img/sharp-wasm32@0.34.5': + '@img/sharp-wasm32@0.35.5': dependencies: - '@emnapi/runtime': 1.10.0 + '@emnapi/runtime': 1.11.3 optional: true - '@img/sharp-win32-arm64@0.34.5': + '@img/sharp-webcontainers-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + + '@img/sharp-win32-arm64@0.35.5': optional: true - '@img/sharp-win32-ia32@0.34.5': + '@img/sharp-win32-ia32@0.35.5': optional: true - '@img/sharp-win32-x64@0.34.5': {} + '@img/sharp-win32-x64@0.35.5': {} '@isaacs/cliui@8.0.2': dependencies: @@ -4400,13 +4443,13 @@ snapshots: jest-util: 30.4.1 slash: 3.0.0 - '@jest/core@30.4.2': + '@jest/core@30.4.2(supports-color@8.1.1)': dependencies: '@jest/console': 30.4.1 '@jest/pattern': 30.4.0 - '@jest/reporters': 30.4.1 + '@jest/reporters': 30.4.1(supports-color@8.1.1) '@jest/test-result': 30.4.1 - '@jest/transform': 30.4.1 + '@jest/transform': 30.4.1(supports-color@8.1.1) '@jest/types': 30.4.1 '@types/node': 25.9.1 ansi-escapes: 4.3.2 @@ -4416,15 +4459,15 @@ snapshots: fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.11 jest-changed-files: 30.4.1 - jest-config: 30.4.2(@types/node@25.9.1) + jest-config: 30.4.2(@types/node@25.9.1)(supports-color@8.1.1) jest-haste-map: 30.4.1 jest-message-util: 30.4.1 jest-regex-util: 30.4.0 jest-resolve: 30.4.1 - jest-resolve-dependencies: 30.4.2 - jest-runner: 30.4.2 - jest-runtime: 30.4.2 - jest-snapshot: 30.4.1 + jest-resolve-dependencies: 30.4.2(supports-color@8.1.1) + jest-runner: 30.4.2(supports-color@8.1.1) + jest-runtime: 30.4.2(supports-color@8.1.1) + jest-snapshot: 30.4.1(supports-color@8.1.1) jest-util: 30.4.1 jest-validate: 30.4.1 jest-watcher: 30.4.1 @@ -4438,7 +4481,7 @@ snapshots: '@jest/diff-sequences@30.4.0': {} - '@jest/environment-jsdom-abstract@30.4.1(jsdom@26.1.0)': + '@jest/environment-jsdom-abstract@30.4.1(jsdom@26.1.0(supports-color@8.1.1))': dependencies: '@jest/environment': 30.4.1 '@jest/fake-timers': 30.4.1 @@ -4447,7 +4490,7 @@ snapshots: '@types/node': 25.9.1 jest-mock: 30.4.1 jest-util: 30.4.1 - jsdom: 26.1.0 + jsdom: 26.1.0(supports-color@8.1.1) '@jest/environment@30.4.1': dependencies: @@ -4460,10 +4503,10 @@ snapshots: dependencies: '@jest/get-type': 30.1.0 - '@jest/expect@30.4.1': + '@jest/expect@30.4.1(supports-color@8.1.1)': dependencies: expect: 30.4.1 - jest-snapshot: 30.4.1 + jest-snapshot: 30.4.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -4478,10 +4521,10 @@ snapshots: '@jest/get-type@30.1.0': {} - '@jest/globals@30.4.1': + '@jest/globals@30.4.1(supports-color@8.1.1)': dependencies: '@jest/environment': 30.4.1 - '@jest/expect': 30.4.1 + '@jest/expect': 30.4.1(supports-color@8.1.1) '@jest/types': 30.4.1 jest-mock: 30.4.1 transitivePeerDependencies: @@ -4492,12 +4535,12 @@ snapshots: '@types/node': 25.9.1 jest-regex-util: 30.4.0 - '@jest/reporters@30.4.1': + '@jest/reporters@30.4.1(supports-color@8.1.1)': dependencies: '@bcoe/v8-coverage': 0.2.3 '@jest/console': 30.4.1 '@jest/test-result': 30.4.1 - '@jest/transform': 30.4.1 + '@jest/transform': 30.4.1(supports-color@8.1.1) '@jest/types': 30.4.1 '@jridgewell/trace-mapping': 0.3.31 '@types/node': 25.9.1 @@ -4507,9 +4550,9 @@ snapshots: glob: 10.5.0 graceful-fs: 4.2.11 istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 + istanbul-lib-instrument: 6.0.3(supports-color@8.1.1) istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 + istanbul-lib-source-maps: 5.0.6(supports-color@8.1.1) istanbul-reports: 3.2.0 jest-message-util: 30.4.1 jest-util: 30.4.1 @@ -4551,12 +4594,12 @@ snapshots: jest-haste-map: 30.4.1 slash: 3.0.0 - '@jest/transform@30.4.1': + '@jest/transform@30.4.1(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@jest/types': 30.4.1 '@jridgewell/trace-mapping': 0.3.31 - babel-plugin-istanbul: 7.0.1 + babel-plugin-istanbul: 7.0.1(supports-color@8.1.1) chalk: 4.1.2 convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 @@ -4608,9 +4651,9 @@ snapshots: dependencies: cross-spawn: 7.0.6 - '@malept/flatpak-bundler@0.4.0': + '@malept/flatpak-bundler@0.4.0(supports-color@8.1.1)': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 9.1.0 lodash: 4.18.1 tmp-promise: 3.0.3 @@ -4948,15 +4991,15 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.59.4(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.59.4 - '@typescript-eslint/type-utils': 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.59.4(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.4(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.59.4 - eslint: 9.39.4(jiti@2.7.0) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -4964,23 +5007,23 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + '@typescript-eslint/parser@8.59.4(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.59.4 '@typescript-eslint/types': 8.59.4 - '@typescript-eslint/typescript-estree': 8.59.4(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.59.4(supports-color@8.1.1)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.59.4 - debug: 4.4.3 - eslint: 9.39.4(jiti@2.7.0) + debug: 4.4.3(supports-color@8.1.1) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.4(typescript@5.9.3)': + '@typescript-eslint/project-service@8.59.4(supports-color@8.1.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.59.4(typescript@5.9.3) '@typescript-eslint/types': 8.59.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4994,13 +5037,13 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.59.4(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.59.4 - '@typescript-eslint/typescript-estree': 8.59.4(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) - debug: 4.4.3 - eslint: 9.39.4(jiti@2.7.0) + '@typescript-eslint/typescript-estree': 8.59.4(supports-color@8.1.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.4(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3) + debug: 4.4.3(supports-color@8.1.1) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -5008,13 +5051,13 @@ snapshots: '@typescript-eslint/types@8.59.4': {} - '@typescript-eslint/typescript-estree@8.59.4(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.59.4(supports-color@8.1.1)(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.59.4(typescript@5.9.3) + '@typescript-eslint/project-service': 8.59.4(supports-color@8.1.1)(typescript@5.9.3) '@typescript-eslint/tsconfig-utils': 8.59.4(typescript@5.9.3) '@typescript-eslint/types': 8.59.4 '@typescript-eslint/visitor-keys': 8.59.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) minimatch: 10.2.5 semver: 7.8.1 tinyglobby: 0.2.16 @@ -5023,13 +5066,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.4(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + '@typescript-eslint/utils@8.59.4(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) '@typescript-eslint/scope-manager': 8.59.4 '@typescript-eslint/types': 8.59.4 - '@typescript-eslint/typescript-estree': 8.59.4(typescript@5.9.3) - eslint: 9.39.4(jiti@2.7.0) + '@typescript-eslint/typescript-estree': 8.59.4(supports-color@8.1.1)(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -5111,11 +5154,11 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.12.2': optional: true - '@vitejs/plugin-react@5.2.0(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))': + '@vitejs/plugin-react@5.2.0(supports-color@8.1.1)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 @@ -5128,6 +5171,9 @@ snapshots: '@xmldom/xmldom@0.9.10': optional: true + '@xmldom/xmldom@0.9.12': + optional: true + abbrev@4.0.0: {} acorn-jsx@5.3.2(acorn@8.16.0): @@ -5138,9 +5184,9 @@ snapshots: adm-zip@0.5.18: {} - agent-base@6.0.2: + agent-base@6.0.2(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -5180,30 +5226,30 @@ snapshots: app-builder-bin@5.0.0-alpha.12: {} - app-builder-lib@26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1): + app-builder-lib@26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1)(supports-color@8.1.1): dependencies: '@develar/schema-utils': 2.6.5 '@electron/asar': 3.4.1 '@electron/fuses': 1.8.0 - '@electron/get': 3.1.0 - '@electron/notarize': 2.5.0 - '@electron/osx-sign': 1.3.3 - '@electron/rebuild': 4.0.4 - '@electron/universal': 2.0.3 - '@malept/flatpak-bundler': 0.4.0 + '@electron/get': 3.1.0(supports-color@8.1.1) + '@electron/notarize': 2.5.0(supports-color@8.1.1) + '@electron/osx-sign': 1.3.3(supports-color@8.1.1) + '@electron/rebuild': 4.0.4(supports-color@8.1.1) + '@electron/universal': 2.0.3(supports-color@8.1.1) + '@malept/flatpak-bundler': 0.4.0(supports-color@8.1.1) '@types/fs-extra': 9.0.13 async-exit-hook: 2.0.1 - builder-util: 26.8.1 - builder-util-runtime: 9.5.1 + builder-util: 26.8.1(supports-color@8.1.1) + builder-util-runtime: 9.5.1(supports-color@8.1.1) chromium-pickle-js: 0.2.0 ci-info: 4.3.1 - debug: 4.4.3 - dmg-builder: 26.8.1(electron-builder-squirrel-windows@26.8.1) + debug: 4.4.3(supports-color@8.1.1) + dmg-builder: 26.8.1(electron-builder-squirrel-windows@26.8.1)(supports-color@8.1.1) dotenv: 16.6.1 dotenv-expand: 11.0.7 ejs: 3.1.10 - electron-builder-squirrel-windows: 26.8.1(dmg-builder@26.8.1) - electron-publish: 26.8.1 + electron-builder-squirrel-windows: 26.8.1(dmg-builder@26.8.1)(supports-color@8.1.1) + electron-publish: 26.8.1(supports-color@8.1.1) fs-extra: 10.1.0 hosted-git-info: 4.1.0 isbinaryfile: 5.0.7 @@ -5258,35 +5304,35 @@ snapshots: postcss: 8.5.15 postcss-value-parser: 4.2.0 - axios@1.16.1: + axios@1.16.1(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - follow-redirects: 1.16.0 + follow-redirects: 1.16.0(debug@4.4.3(supports-color@8.1.1)) form-data: 4.0.5 - https-proxy-agent: 5.0.1 + https-proxy-agent: 5.0.1(supports-color@8.1.1) proxy-from-env: 2.1.0 transitivePeerDependencies: - debug - supports-color - babel-jest@30.4.1(@babel/core@7.29.0): + babel-jest@30.4.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 - '@jest/transform': 30.4.1 + '@babel/core': 7.29.0(supports-color@8.1.1) + '@jest/transform': 30.4.1(supports-color@8.1.1) '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.4.0(@babel/core@7.29.0) + babel-plugin-istanbul: 7.0.1(supports-color@8.1.1) + babel-preset-jest: 30.4.0(@babel/core@7.29.0(supports-color@8.1.1)) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 transitivePeerDependencies: - supports-color - babel-plugin-istanbul@7.0.1: + babel-plugin-istanbul@7.0.1(supports-color@8.1.1): dependencies: '@babel/helper-plugin-utils': 7.28.6 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.6 - istanbul-lib-instrument: 6.0.3 + istanbul-lib-instrument: 6.0.3(supports-color@8.1.1) test-exclude: 6.0.0 transitivePeerDependencies: - supports-color @@ -5295,30 +5341,30 @@ snapshots: dependencies: '@types/babel__core': 7.20.5 - babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) - - babel-preset-jest@30.4.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0(supports-color@8.1.1)): + dependencies: + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0(supports-color@8.1.1)) + + babel-preset-jest@30.4.0(@babel/core@7.29.0(supports-color@8.1.1)): + dependencies: + '@babel/core': 7.29.0(supports-color@8.1.1) babel-plugin-jest-hoist: 30.4.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0(supports-color@8.1.1)) balanced-match@1.0.2: {} @@ -5368,25 +5414,25 @@ snapshots: ieee754: 1.2.1 optional: true - builder-util-runtime@9.5.1: + builder-util-runtime@9.5.1(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) sax: 1.6.0 transitivePeerDependencies: - supports-color - builder-util@26.8.1: + builder-util@26.8.1(supports-color@8.1.1): dependencies: 7zip-bin: 5.2.0 '@types/debug': 4.1.13 app-builder-bin: 5.0.0-alpha.12 - builder-util-runtime: 9.5.1 + builder-util-runtime: 9.5.1(supports-color@8.1.1) chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 10.1.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + http-proxy-agent: 7.0.2(supports-color@8.1.1) + https-proxy-agent: 7.0.6(supports-color@8.1.1) js-yaml: 4.1.1 sanitize-filename: 1.6.4 source-map-support: 0.5.21 @@ -5521,9 +5567,11 @@ snapshots: whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - debug@4.4.3: + debug@4.4.3(supports-color@8.1.1): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 decimal.js@10.6.0: {} @@ -5567,10 +5615,10 @@ snapshots: minimatch: 3.1.5 p-limit: 3.1.0 - dmg-builder@26.8.1(electron-builder-squirrel-windows@26.8.1): + dmg-builder@26.8.1(electron-builder-squirrel-windows@26.8.1)(supports-color@8.1.1): dependencies: - app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1) - builder-util: 26.8.1 + app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1)(supports-color@8.1.1) + builder-util: 26.8.1(supports-color@8.1.1) fs-extra: 10.1.0 iconv-lite: 0.6.3 js-yaml: 4.1.1 @@ -5614,23 +5662,23 @@ snapshots: dependencies: jake: 10.9.4 - electron-builder-squirrel-windows@26.8.1(dmg-builder@26.8.1): + electron-builder-squirrel-windows@26.8.1(dmg-builder@26.8.1)(supports-color@8.1.1): dependencies: - app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1) - builder-util: 26.8.1 - electron-winstaller: 5.4.0 + app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1)(supports-color@8.1.1) + builder-util: 26.8.1(supports-color@8.1.1) + electron-winstaller: 5.4.0(supports-color@8.1.1) transitivePeerDependencies: - dmg-builder - supports-color - electron-builder@26.8.1(electron-builder-squirrel-windows@26.8.1): + electron-builder@26.8.1(electron-builder-squirrel-windows@26.8.1)(supports-color@8.1.1): dependencies: - app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1) - builder-util: 26.8.1 - builder-util-runtime: 9.5.1 + app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1)(supports-color@8.1.1) + builder-util: 26.8.1(supports-color@8.1.1) + builder-util-runtime: 9.5.1(supports-color@8.1.1) chalk: 4.1.2 ci-info: 4.4.0 - dmg-builder: 26.8.1(electron-builder-squirrel-windows@26.8.1) + dmg-builder: 26.8.1(electron-builder-squirrel-windows@26.8.1)(supports-color@8.1.1) fs-extra: 10.1.0 lazy-val: 1.0.5 simple-update-notifier: 2.0.0 @@ -5639,11 +5687,11 @@ snapshots: - electron-builder-squirrel-windows - supports-color - electron-publish@26.8.1: + electron-publish@26.8.1(supports-color@8.1.1): dependencies: '@types/fs-extra': 9.0.13 - builder-util: 26.8.1 - builder-util-runtime: 9.5.1 + builder-util: 26.8.1(supports-color@8.1.1) + builder-util-runtime: 9.5.1(supports-color@8.1.1) chalk: 4.1.2 form-data: 4.0.5 fs-extra: 10.1.0 @@ -5654,22 +5702,22 @@ snapshots: electron-to-chromium@1.5.361: {} - electron-winstaller@5.4.0: + electron-winstaller@5.4.0(supports-color@8.1.1): dependencies: '@electron/asar': 3.4.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fs-extra: 7.0.1 lodash: 4.18.1 temp: 0.9.4 optionalDependencies: - '@electron/windows-sign': 1.2.2 + '@electron/windows-sign': 1.2.2(supports-color@8.1.1) transitivePeerDependencies: - supports-color - electron@43.1.0: + electron@43.1.0(supports-color@8.1.1): dependencies: '@electron-internal/extract-zip': 1.0.4 - '@electron/get': 5.0.0 + '@electron/get': 5.0.0(supports-color@8.1.1) '@types/node': 24.13.3 transitivePeerDependencies: - supports-color @@ -5754,20 +5802,20 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-plugin-react-hooks@7.1.1(eslint@9.39.4(jiti@2.7.0)): + eslint-plugin-react-hooks@7.1.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/parser': 7.29.3 - eslint: 9.39.4(jiti@2.7.0) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) hermes-parser: 0.25.1 zod: 4.4.3 zod-validation-error: 4.0.2(zod@4.4.3) transitivePeerDependencies: - supports-color - eslint-plugin-react-refresh@0.4.26(eslint@9.39.4(jiti@2.7.0)): + eslint-plugin-react-refresh@0.4.26(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)): dependencies: - eslint: 9.39.4(jiti@2.7.0) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) eslint-scope@8.4.0: dependencies: @@ -5780,14 +5828,14 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@9.39.4(jiti@2.7.0): + eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.2 + '@eslint/config-array': 0.21.2(supports-color@8.1.1) '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.5 + '@eslint/eslintrc': 3.3.5(supports-color@8.1.1) '@eslint/js': 9.39.4 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.8 @@ -5797,7 +5845,7 @@ snapshots: ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -5853,9 +5901,9 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - exifreader@4.39.1: + exifreader@4.50.0: optionalDependencies: - '@xmldom/xmldom': 0.9.10 + '@xmldom/xmldom': 0.9.12 exit-x@0.2.2: {} @@ -5912,7 +5960,9 @@ snapshots: flatted@3.4.2: {} - follow-redirects@1.16.0: {} + follow-redirects@1.16.0(debug@4.4.3(supports-color@8.1.1)): + optionalDependencies: + debug: 4.4.3(supports-color@8.1.1) foreground-child@3.3.1: dependencies: @@ -6109,10 +6159,10 @@ snapshots: http-cache-semantics@4.2.0: {} - http-proxy-agent@7.0.2: + http-proxy-agent@7.0.2(supports-color@8.1.1): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -6121,17 +6171,17 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - https-proxy-agent@5.0.1: + https-proxy-agent@5.0.1(supports-color@8.1.1): dependencies: - agent-base: 6.0.2 - debug: 4.4.3 + agent-base: 6.0.2(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.6: + https-proxy-agent@7.0.6(supports-color@8.1.1): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -6207,9 +6257,9 @@ snapshots: istanbul-lib-coverage@3.2.2: {} - istanbul-lib-instrument@6.0.3: + istanbul-lib-instrument@6.0.3(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/parser': 7.29.3 '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 @@ -6223,10 +6273,10 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@5.0.6: + istanbul-lib-source-maps@5.0.6(supports-color@8.1.1): dependencies: '@jridgewell/trace-mapping': 0.3.31 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -6254,10 +6304,10 @@ snapshots: jest-util: 30.4.1 p-limit: 3.1.0 - jest-circus@30.4.2: + jest-circus@30.4.2(supports-color@8.1.1): dependencies: '@jest/environment': 30.4.1 - '@jest/expect': 30.4.1 + '@jest/expect': 30.4.1(supports-color@8.1.1) '@jest/test-result': 30.4.1 '@jest/types': 30.4.1 '@types/node': 25.9.1 @@ -6268,8 +6318,8 @@ snapshots: jest-each: 30.4.1 jest-matcher-utils: 30.4.1 jest-message-util: 30.4.1 - jest-runtime: 30.4.2 - jest-snapshot: 30.4.1 + jest-runtime: 30.4.2(supports-color@8.1.1) + jest-snapshot: 30.4.1(supports-color@8.1.1) jest-util: 30.4.1 p-limit: 3.1.0 pretty-format: 30.4.1 @@ -6280,15 +6330,15 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@30.4.2(@types/node@25.9.1): + jest-cli@30.4.2(@types/node@25.9.1)(supports-color@8.1.1): dependencies: - '@jest/core': 30.4.2 + '@jest/core': 30.4.2(supports-color@8.1.1) '@jest/test-result': 30.4.1 '@jest/types': 30.4.1 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.4.2(@types/node@25.9.1) + jest-config: 30.4.2(@types/node@25.9.1)(supports-color@8.1.1) jest-util: 30.4.1 jest-validate: 30.4.1 yargs: 17.7.2 @@ -6299,25 +6349,25 @@ snapshots: - supports-color - ts-node - jest-config@30.4.2(@types/node@25.9.1): + jest-config@30.4.2(@types/node@25.9.1)(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@jest/get-type': 30.1.0 '@jest/pattern': 30.4.0 '@jest/test-sequencer': 30.4.1 '@jest/types': 30.4.1 - babel-jest: 30.4.1(@babel/core@7.29.0) + babel-jest: 30.4.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) chalk: 4.1.2 ci-info: 4.4.0 deepmerge: 4.3.1 glob: 10.5.0 graceful-fs: 4.2.11 - jest-circus: 30.4.2 + jest-circus: 30.4.2(supports-color@8.1.1) jest-docblock: 30.4.0 jest-environment-node: 30.4.1 jest-regex-util: 30.4.0 jest-resolve: 30.4.1 - jest-runner: 30.4.2 + jest-runner: 30.4.2(supports-color@8.1.1) jest-util: 30.4.1 jest-validate: 30.4.1 parse-json: 5.2.0 @@ -6349,11 +6399,11 @@ snapshots: jest-util: 30.4.1 pretty-format: 30.4.1 - jest-environment-jsdom@30.4.1: + jest-environment-jsdom@30.4.1(supports-color@8.1.1): dependencies: '@jest/environment': 30.4.1 - '@jest/environment-jsdom-abstract': 30.4.1(jsdom@26.1.0) - jsdom: 26.1.0 + '@jest/environment-jsdom-abstract': 30.4.1(jsdom@26.1.0(supports-color@8.1.1)) + jsdom: 26.1.0(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - supports-color @@ -6421,10 +6471,10 @@ snapshots: jest-regex-util@30.4.0: {} - jest-resolve-dependencies@30.4.2: + jest-resolve-dependencies@30.4.2(supports-color@8.1.1): dependencies: jest-regex-util: 30.4.0 - jest-snapshot: 30.4.1 + jest-snapshot: 30.4.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -6439,12 +6489,12 @@ snapshots: slash: 3.0.0 unrs-resolver: 1.12.2 - jest-runner@30.4.2: + jest-runner@30.4.2(supports-color@8.1.1): dependencies: '@jest/console': 30.4.1 '@jest/environment': 30.4.1 '@jest/test-result': 30.4.1 - '@jest/transform': 30.4.1 + '@jest/transform': 30.4.1(supports-color@8.1.1) '@jest/types': 30.4.1 '@types/node': 25.9.1 chalk: 4.1.2 @@ -6457,7 +6507,7 @@ snapshots: jest-leak-detector: 30.4.1 jest-message-util: 30.4.1 jest-resolve: 30.4.1 - jest-runtime: 30.4.2 + jest-runtime: 30.4.2(supports-color@8.1.1) jest-util: 30.4.1 jest-watcher: 30.4.1 jest-worker: 30.4.1 @@ -6466,14 +6516,14 @@ snapshots: transitivePeerDependencies: - supports-color - jest-runtime@30.4.2: + jest-runtime@30.4.2(supports-color@8.1.1): dependencies: '@jest/environment': 30.4.1 '@jest/fake-timers': 30.4.1 - '@jest/globals': 30.4.1 + '@jest/globals': 30.4.1(supports-color@8.1.1) '@jest/source-map': 30.0.1 '@jest/test-result': 30.4.1 - '@jest/transform': 30.4.1 + '@jest/transform': 30.4.1(supports-color@8.1.1) '@jest/types': 30.4.1 '@types/node': 25.9.1 chalk: 4.1.2 @@ -6486,26 +6536,26 @@ snapshots: jest-mock: 30.4.1 jest-regex-util: 30.4.0 jest-resolve: 30.4.1 - jest-snapshot: 30.4.1 + jest-snapshot: 30.4.1(supports-color@8.1.1) jest-util: 30.4.1 slash: 3.0.0 strip-bom: 4.0.0 transitivePeerDependencies: - supports-color - jest-snapshot@30.4.1: + jest-snapshot@30.4.1(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/generator': 7.29.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/types': 7.29.0 '@jest/expect-utils': 30.4.1 '@jest/get-type': 30.1.0 '@jest/snapshot-utils': 30.4.1 - '@jest/transform': 30.4.1 + '@jest/transform': 30.4.1(supports-color@8.1.1) '@jest/types': 30.4.1 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0(supports-color@8.1.1)) chalk: 4.1.2 expect: 30.4.1 graceful-fs: 4.2.11 @@ -6556,12 +6606,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@30.4.2(@types/node@25.9.1): + jest@30.4.2(@types/node@25.9.1)(supports-color@8.1.1): dependencies: - '@jest/core': 30.4.2 + '@jest/core': 30.4.2(supports-color@8.1.1) '@jest/types': 30.4.1 import-local: 3.2.0 - jest-cli: 30.4.2(@types/node@25.9.1) + jest-cli: 30.4.2(@types/node@25.9.1)(supports-color@8.1.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -6592,14 +6642,14 @@ snapshots: dependencies: argparse: 2.0.1 - jsdom@26.1.0: + jsdom@26.1.0(supports-color@8.1.1): dependencies: cssstyle: 4.6.0 data-urls: 5.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + http-proxy-agent: 7.0.2(supports-color@8.1.1) + https-proxy-agent: 7.0.6(supports-color@8.1.1) is-potential-custom-element-name: 1.0.1 nwsapi: 2.2.23 parse5: 7.3.0 @@ -7056,9 +7106,9 @@ snapshots: react@19.2.6: {} - read-binary-file-arch@1.0.6: + read-binary-file-arch@1.0.6(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -7165,6 +7215,8 @@ snapshots: semver@7.8.1: {} + semver@7.8.5: {} + serialize-error@7.0.1: dependencies: type-fest: 0.13.1 @@ -7174,36 +7226,38 @@ snapshots: dependencies: type-fest: 0.20.2 - sharp@0.34.5: + sharp@0.35.5(@types/node@25.9.1): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 - semver: 7.8.1 + semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 + '@img/sharp-darwin-arm64': 0.35.5 + '@img/sharp-darwin-x64': 0.35.5 + '@img/sharp-freebsd-wasm32': 0.35.5 + '@img/sharp-libvips-darwin-arm64': 1.3.4 + '@img/sharp-libvips-darwin-x64': 1.3.4 + '@img/sharp-libvips-linux-arm': 1.3.4 + '@img/sharp-libvips-linux-arm64': 1.3.4 + '@img/sharp-libvips-linux-ppc64': 1.3.4 + '@img/sharp-libvips-linux-riscv64': 1.3.4 + '@img/sharp-libvips-linux-s390x': 1.3.4 + '@img/sharp-libvips-linux-x64': 1.3.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + '@img/sharp-linux-arm': 0.35.5 + '@img/sharp-linux-arm64': 0.35.5 + '@img/sharp-linux-ppc64': 0.35.5 + '@img/sharp-linux-riscv64': 0.35.5 + '@img/sharp-linux-s390x': 0.35.5 + '@img/sharp-linux-x64': 0.35.5 + '@img/sharp-linuxmusl-arm64': 0.35.5 + '@img/sharp-linuxmusl-x64': 0.35.5 + '@img/sharp-webcontainers-wasm32': 0.35.5 + '@img/sharp-win32-arm64': 0.35.5 + '@img/sharp-win32-ia32': 0.35.5 + '@img/sharp-win32-x64': 0.35.5 + '@types/node': 25.9.1 shebang-command@2.0.0: dependencies: @@ -7293,9 +7347,9 @@ snapshots: strip-json-comments@3.1.1: {} - sumchecker@3.0.1: + sumchecker@3.0.1(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -7389,12 +7443,12 @@ snapshots: dependencies: typescript: 5.9.3 - ts-jest@29.4.11(@babel/core@7.29.0)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.0))(jest-util@30.4.1)(jest@30.4.2(@types/node@25.9.1))(typescript@5.9.3): + ts-jest@29.4.11(@babel/core@7.29.0(supports-color@8.1.1))(@jest/transform@30.4.1(supports-color@8.1.1))(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(esbuild@0.27.7)(jest-util@30.4.1)(jest@30.4.2(@types/node@25.9.1)(supports-color@8.1.1))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.9 - jest: 30.4.2(@types/node@25.9.1) + jest: 30.4.2(@types/node@25.9.1)(supports-color@8.1.1) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -7403,10 +7457,11 @@ snapshots: typescript: 5.9.3 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.29.0 - '@jest/transform': 30.4.1 + '@babel/core': 7.29.0(supports-color@8.1.1) + '@jest/transform': 30.4.1(supports-color@8.1.1) '@jest/types': 30.4.1 - babel-jest: 30.4.1(@babel/core@7.29.0) + babel-jest: 30.4.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + esbuild: 0.27.7 jest-util: 30.4.1 tslib@2.8.1: {} @@ -7517,9 +7572,9 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - wait-on@9.0.10: + wait-on@9.0.10(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - axios: 1.16.1 + axios: 1.16.1(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1) joi: 18.2.1 lodash: 4.18.1 minimist: 1.2.8 diff --git a/src/test/cameraMatch.test.ts b/src/test/cameraMatch.test.ts index 8d3e846..e66fa07 100644 --- a/src/test/cameraMatch.test.ts +++ b/src/test/cameraMatch.test.ts @@ -187,8 +187,8 @@ describe('cameraMatch fitting internals', () => { describe('decodeToGrid', () => { it('box-averages a uniform buffer to its exact value', () => { - const w = 400; - const h = 300; + const w = GW * 2; // decodeToGrid samples every 2nd pixel, so each cell needs at least one + const h = GH * 2; const u16 = new Uint16Array(w * h * 3).fill(32768); const grid = cm.decodeToGrid(u16, w, h); expect(grid[0]).toBeCloseTo(32768 / 65535, 5); @@ -266,3 +266,54 @@ describe('cameraMatch preview registration (lens-distortion-corrected previews)' expect(model.M.flat().every(Number.isFinite)).toBe(true); }); }); + +describe('cameraMatch geometry correction (decode resampled to the camera frame)', () => { + const geo = require('../../electron/cameraMatchGeometry.cjs') as any; + const W = 240, H = 180, ASPECT_G = W / H; + // A smooth scene defined in the corrected (camera JPEG) frame, x,y in [-0.5,0.5]. + const scene = (x: number, y: number) => 0.5 + 0.3 * Math.sin(x * 9) * Math.cos(y * 7) + 0.15 * x; + + it('the inverse table undoes warpPoint along the radius', () => { + const k = 0.1; + const lut = geo.buildInverseRadialLut(k); + for (const rq of [0.1, 0.4, 0.7, 1]) { + const i = rq * rq * geo.LUT_SIZE; + const s = lut[Math.floor(i)] + (lut[Math.floor(i) + 1] - lut[Math.floor(i)]) * (i - Math.floor(i)); + const rp = rq * s; + expect(rp * (1 + k * rp * rp)).toBeCloseTo(rq, 4); + } + }); + + it('resamples a distorted decode back onto the scene', () => { + const k = 0.1; + // The decode shows, at point p, the scene at warpPoint(p). + const dec = new Uint16Array(W * H * 3); + for (let y = 0; y < H; y++) { + for (let x = 0; x < W; x++) { + const [wx, wy] = cm.warpPoint((x + 0.5) / W - 0.5, (y + 0.5) / H - 0.5, k, ASPECT_G); + const v = Math.round(Math.min(1, Math.max(0, scene(wx, wy))) * 65535); + dec.fill(v, (y * W + x) * 3, (y * W + x) * 3 + 3); + } + } + const out = geo.undistortRGB16(dec, W, H, k); + let worstBefore = 0, worstAfter = 0; + for (let y = 0; y < H; y++) { + for (let x = 0; x < W; x++) { + const want = Math.min(1, Math.max(0, scene((x + 0.5) / W - 0.5, (y + 0.5) / H - 0.5))); + worstBefore = Math.max(worstBefore, Math.abs(dec[(y * W + x) * 3] / 65535 - want)); + worstAfter = Math.max(worstAfter, Math.abs(out[(y * W + x) * 3] / 65535 - want)); + } + } + expect(worstBefore).toBeGreaterThan(0.05); // the distortion is visible without the correction + expect(worstAfter).toBeLessThan(0.005); + }); + + it('fills only the requested rows, so strips can be computed independently', () => { + const src = new Uint16Array(W * H * 3).map((_, i) => (i * 7919) % 65536); + const whole = geo.undistortRGB16(src, W, H, 0.08); + const strips = new Uint16Array(src.length); + geo.undistortRGB16(src, W, H, 0.08, strips, 0, 70); + geo.undistortRGB16(src, W, H, 0.08, strips, 70, H); + expect(Array.from(strips)).toEqual(Array.from(whole)); + }); +});