diff --git a/.gitignore b/.gitignore index 251783b..39735de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ node_modules/ out/ dist/ +runtime/ +kadr-fragments-seed/ *.log .claude/ *.autosave.kadr diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..7f00d49 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,99 @@ +# Установка Kadr + +## Для пользователя — готовый архив (рекомендуется) + +Не нужен ни `npm install`, ни компиляторы, ни системный ffmpeg. Особенно +актуально для immutable-дистрибутивов (Bazzite, Silverblue, Kinoite). + +**tar.gz:** +```bash +tar -xzf kadr-<версия>-linux-x64.tar.gz +cd kadr-<версия>-linux-x64 +./kadr +``` + +**AppImage:** +```bash +chmod +x Kadr-<версия>-linux-x86_64.AppImage +./Kadr-<версия>-linux-x86_64.AppImage +# нет FUSE? → ./Kadr-*.AppImage --appimage-extract-and-run +``` + +Что работает сразу, без доустановки: +- **Ядро** (импорт / превью / экспорт) — встроены статические `ffmpeg`/`ffprobe`. +- **Субтитры** (faster-whisper) — встроен переносимый Python. При первом + использовании один раз качается ML-модель (нужна сеть). +- **Remotion-фрагменты** — встроены Node и пред-установленные зависимости. + Если сборка не «прогрела» headless-Chromium, он один раз докачается при + первом рендере фрагмента (сеть; но уже без системного Node). + +Опционально (ставит сам пользователь): **Claude-панель** требует установленного +`claude` CLI в `PATH` — это личный инструмент/аккаунт, встроить его нельзя. + +Только `linux-x64` (glibc). Для arm64 / musl нужна отдельная сборка. + +### Запуск из меню приложений (без терминала) + +**AppImage** — уже запускается двойным кликом из файлового менеджера (нужен бит +«исполняемый»: ПКМ → Свойства → Разрешения). Чтобы появилась постоянная иконка в +меню приложений и автообновление — интегрируй его через **Gearlever** (штатный +GUI-способ для Bazzite/атомарных систем): +```bash +flatpak install flathub it.mijorus.gearlever +``` +Открой Gearlever → перетащи в него `Kadr-*.AppImage` → «Add to menu». Дальше Kadr +запускается из меню как обычное приложение. + +**tar.gz** — распакуй, положи папку в постоянное место и один раз запусти +`install.sh`: +```bash +tar -xzf kadr-<версия>-linux-x64.tar.gz +sudo mv kadr-<версия>-linux-x64 /opt/kadr # любое постоянное место +bash /opt/kadr/install.sh # БЕЗ sudo — пишет в ~/.local +``` +После этого «Kadr» появляется в меню/поиске KDE с логотипом и запускается кликом, +без терминала — и всё (звук, экспорт, субтитры, Remotion, Claude-панель) работает +без ручной настройки. `install.sh` запускай **своим пользователем, не через sudo** +(он пишет только в `~/.local/share`, даже если приложение лежит в `/opt`). +Перенёс папку — запусти `bash /opt/kadr/install.sh` ещё раз. Убрать ярлык: +`bash /opt/kadr/install.sh --uninstall`. + +--- + +## Для мейнтейнера — сборка архива + +Сборку нужно делать в окружении с тулчейном (компилятор для `node-pty`), а не на +голом immutable-хосте. Штатный путь — distrobox: + +```bash +distrobox create --name kadr-build --image fedora:42 +distrobox enter kadr-build +sudo dnf install -y gcc-c++ make python3 nodejs npm tar xz which curl + +cd ~/Projects/kadr +npm run pack # = bash scripts/pack.sh +``` + +Артефакты появятся в `dist/`. Скрипт сам: +1. собирает приложение (`npm ci` + `electron-vite build`); +2. кладёт статический ffmpeg/ffprobe, переносимый Node и Python+faster-whisper в + `runtime/`; +3. пред-устанавливает зависимости Remotion в `kadr-fragments-seed/`; +4. пересобирает `node-pty` под ABI встроенного Electron и пакует + tar.gz + AppImage через electron-builder. + +Флаги `scripts/pack.sh`: +- `--lite` — только ядро + ffmpeg (без whisper и Remotion; ~в разы меньше размер); +- `--model ` — предвложить модель субтитров (`base`, `small`, …) для + полностью офлайн-субтитров; +- `--skip-build` — переиспользовать существующий `out/`. + +Версии вложенных Node / Python / ffmpeg закреплены вверху `scripts/pack.sh` — +меняются там. Размер полного архива ориентировочно ~0.5–0.7 ГБ. + +> Как это устроено под капотом: `electron/runtime-env.ts` (первый импорт +> `main.ts`) в упакованной сборке добавляет `resources/runtime/bin` в `PATH` и +> выставляет `KADR_FFMPEG`/`KADR_FFPROBE`, поэтому все внешние вызовы +> (`ffmpeg`, `python3`, `npm`, `npx`, `node`) резолвятся во вложенные бинарники. +> Упаковка идёт без asar — `electron/mcp-bridge.cjs` и `scripts/transcribe.py` +> запускаются внешними процессами по реальному пути и внутри asar не читались бы. diff --git a/build/icon.png b/build/icon.png new file mode 100644 index 0000000..05dbfb3 Binary files /dev/null and b/build/icon.png differ diff --git a/build/icon.svg b/build/icon.svg new file mode 100644 index 0000000..45455ea --- /dev/null +++ b/build/icon.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/electron-builder.yml b/electron-builder.yml new file mode 100644 index 0000000..a0cd63a --- /dev/null +++ b/electron-builder.yml @@ -0,0 +1,67 @@ +# electron-builder config for the self-contained Linux archive. +# Run via scripts/pack.sh (inside a distrobox with a build toolchain), which +# stages runtime/ and kadr-fragments-seed/ before invoking electron-builder. +appId: com.blacktriangle.kadr +productName: Kadr +copyright: © Black Triangle — GPL-3.0 + +directories: + output: dist + buildResources: build + +# node/python/npx read these files by real on-disk path (app.getAppPath()), +# and external processes can't read inside an asar — so ship unpacked. +asar: false + +# Whitelist the app payload. electron-builder always adds production +# node_modules (pruned of devDependencies) on top of this, which mcp-bridge.cjs +# needs at runtime (@modelcontextprotocol/sdk). +files: + - out/**/* + - electron/mcp-bridge.cjs + # every one of these is read from disk at runtime by app.getAppPath(), so a + # build without them starts fine and then fails the feature: the Claude panel + # cannot sync its skill ("skill sync failed: ENOENT"), and voice-defect + # detection has no driver, no package and no model. 2.2 MB in total, of which + # 1.9 MB is python/models. + - electron/kadr-skill.md + - scripts/transcribe.py + - scripts/ttsqc_run.py + - python/**/* + - package.json + +# Bundled runtime + Remotion seed land under resources/ (process.resourcesPath). +# Both dirs are produced by scripts/pack.sh; guarded there so a "lite" build can +# omit kadr-fragments-seed. +extraResources: + - from: runtime + to: runtime + - from: kadr-fragments-seed + to: kadr-fragments-seed + +linux: + target: + - tar.gz + - AppImage + category: AudioVideo + icon: build/icon.png + synopsis: GPU-accelerated multi-track video editor + artifactName: ${name}-${version}-linux-${arch}.${ext} + # WM_CLASS of the Electron window — lets KDE/GNOME match the running window + # to this launcher (correct task-bar icon, grouping). + desktop: + entry: + StartupWMClass: Kadr + Categories: AudioVideo;AudioVideoEditing;Video; + +# The AppImage is self-contained (its icon + .desktop are embedded — integrate +# it with Gearlever/appimaged). The tar.gz is just files, so ship a one-shot +# helper + icon at the archive root that registers a menu entry per-user. +extraFiles: + - from: scripts/install-desktop.sh + to: install.sh + - from: build/icon.png + to: kadr.png + +# node-pty is rebuilt against the bundled Electron's ABI at pack time. +npmRebuild: true diff --git a/electron/cacheKeys.ts b/electron/cacheKeys.ts index 300f917..eee8a97 100644 --- a/electron/cacheKeys.ts +++ b/electron/cacheKeys.ts @@ -30,8 +30,10 @@ export function mediaCacheKey( export const proxySuffix = (alpha?: boolean) => (alpha ? ':a' : '') /** decoded: packed colour+matte, plain alpha, or neither */ +// ':p2' — packed-matte generation: intermediates packed before the matte +// range fix (double pc→tv compression) must miss the cache and re-pack. export const decodedSuffix = (opts?: { alpha?: boolean; packed?: boolean }) => - opts?.packed ? ':p' : opts?.alpha ? ':a' : '' + opts?.packed ? ':p2' : opts?.alpha ? ':a' : '' /** reversed: the same source cut differently is a different render */ export const reverseSuffix = (start: number, duration: number) => diff --git a/electron/ffmpeg.ts b/electron/ffmpeg.ts index a72f231..dc30353 100644 --- a/electron/ffmpeg.ts +++ b/electron/ffmpeg.ts @@ -44,9 +44,11 @@ export async function probeMedia(path: string): Promise { width: video?.width || 0, height: video?.height || 0, fps: fps || 30, - hasAudio: !!audio + hasAudio: !!audio, + audioCodec: audio?.codec_name } if (kind === 'video' && video?.codec_name) asset.codec = video.codec_name + try { asset.mtimeMs = Math.round((await fsp.stat(path)).mtimeMs) } catch { /* stat optional */ } if (kind === 'video') { // alpha travels two ways: an alpha pixel format (yuva…, rgba, prores // 4444) or WebM's container-level alpha_mode tag (vp8/vp9 alpha planes — @@ -144,9 +146,19 @@ export function makeProxy( out: string, duration: number, onProgress?: (p: number) => void, - opts?: { alpha?: boolean; codec?: string } + opts?: { alpha?: boolean; codec?: string; audioOnly?: boolean } ): Promise { - const args = opts?.alpha ? [ + // audioOnly: the source has no usable video (a music/voice file) but an audio + // codec Chromium can't decode (ac3/dts/…) — transcode just the audio to AAC + // so the preview element has sound. alpha: a VP9+alpha WebM keeps the + // transparency. Otherwise a normal 540p video+AAC proxy. + const args = opts?.audioOnly ? [ + '-y', '-v', 'error', '-progress', 'pipe:1', + '-i', src, + '-vn', '-c:a', 'aac', '-b:a', '160k', + '-movflags', '+faststart', + out + ] : opts?.alpha ? [ '-y', '-v', 'error', '-progress', 'pipe:1', ...alphaInputArgs(opts.codec), '-i', src, @@ -334,7 +346,14 @@ export async function makeDecoded( `[c]format=yuv420p,scale=in_color_matrix=${opts.matrix || 'bt601'}:out_color_matrix=bt709[col];` + // the matte rides in the luma plane, mapped into the same limited range // the decoder will expand back — verified to round-trip bit-exactly - '[a]alphaextract,format=yuv420p,scale=in_range=pc:out_range=tv[m];' + + // Range-compress the matte BEFORE format=yuv420p. Newer swscale already + // maps gray→yuv420p into limited range, so a scale=pc→tv placed after it + // compressed the matte TWICE (255 → 235 → 218): every opaque area of an + // alpha source composited at ~92 % (a 255 red came out at 229). With the + // explicit scale first, format= sees a tv-tagged input and leaves it alone + // on new swscale, while old swscale (a plain copy) still gets the one + // compression it needs — 235/127/71 for α 255/129/64 on both. + '[a]alphaextract,scale=in_range=pc:out_range=tv,format=yuv420p[m];' + '[col][m]vstack=inputs=2[v]', '-map', '[v]', '-an', '-c:v', 'libx264', '-preset', 'veryfast', '-qp', '0', '-pix_fmt', 'yuv420p', @@ -644,6 +663,11 @@ export class ExportMuxer { const outDur = s.duration / speed // timeline-domain length after atempo const chain = [ 'aformat=sample_fmts=fltp:sample_rates=48000:channel_layouts=stereo', + // Normalize the start PTS to 0 (a lavfi source already is). Defensive: + // some containers hand filter_complex a non-zero first PTS after an + // input seek. NOTE this alone does NOT fix the "clip jumps to t=0" bug — + // see the pad below for the actual cause. + 'asetpts=PTS-STARTPTS', `volume=${s.gain.toFixed(4)}`, ...(Math.abs(speed - 1) > 1e-4 ? atempoChain(speed) : []), ...(s.fadeIn > 0.001 ? [`afade=t=in:st=0:d=${Math.min(s.fadeIn, outDur).toFixed(3)}`] : []), @@ -651,8 +675,16 @@ export class ExportMuxer { ? [`afade=t=out:st=${Math.max(0, outDur - s.fadeOut).toFixed(3)}:d=${Math.min(s.fadeOut, outDur).toFixed(3)}`] : []), `adelay=${ms}|${ms}`, - 'apad', - `atrim=0:${job.duration.toFixed(3)}` + // Pad each stream to exactly job.duration with a BOUNDED pad so amix + // (duration=longest) reaches EOF — instead of an unbounded `apad` + // followed by `atrim=0:job.duration`. That trailing atrim is the real + // cause of the audio-collapse bug: on an input-seeked segment + // (`-ss inPoint -i file`, i.e. inPoint > 0) it throws the delayed audio + // back to t=0 and leaves its slot silent — even with PTS re-zeroed + // (verified: asetpts=N/SR/TB + atrim still collapses; any chain without + // the trailing atrim places the clip correctly). Segments are already + // range-bounded, so nothing here can exceed job.duration. + `apad=whole_dur=${job.duration.toFixed(3)}` ] filters.push(`[${idx}:a]${chain.join(',')}[a${i}]`) labels.push(`[a${i}]`) diff --git a/electron/fragments.ts b/electron/fragments.ts index 98c6d84..7da8935 100644 --- a/electron/fragments.ts +++ b/electron/fragments.ts @@ -295,21 +295,33 @@ async function ensureWorkspace( let installed = false if (!existsSync(join(WORKSPACE, 'node_modules', 'remotion'))) { onProgress?.('install', 0) - const extraEnv = await netEnv() - await new Promise((resolve, reject) => { - const child = spawn('npm', ['install', '--no-audit', '--no-fund'], { - cwd: WORKSPACE, - env: { ...process.env, ...extraEnv }, - stdio: ['ignore', 'pipe', 'pipe'] - }) - let err = '' - child.stderr.on('data', (c) => { err += c }) - child.on('error', reject) - child.on('close', (code) => { - if (code === 0) resolve() - else reject(new Error(`npm install failed (${code}): ${err.slice(-600)}`)) + // Packaged builds ship a pre-installed node_modules (resources/ + // kadr-fragments-seed) so the first fragment needs neither host npm nor a + // ~150 MB network install. Copy it once; if the seed's browser cache came + // along inside node_modules it travels for free. No seed (dev, or a lite + // build) → fall back to a live `npm install` with the bundled node/npm. + const seed = app.isPackaged + ? join(process.resourcesPath, 'kadr-fragments-seed', 'node_modules') + : '' + if (seed && existsSync(join(seed, 'remotion'))) { + await fs.cp(seed, join(WORKSPACE, 'node_modules'), { recursive: true }) + } else { + const extraEnv = await netEnv() + await new Promise((resolve, reject) => { + const child = spawn('npm', ['install', '--no-audit', '--no-fund'], { + cwd: WORKSPACE, + env: { ...process.env, ...extraEnv }, + stdio: ['ignore', 'pipe', 'pipe'] + }) + let err = '' + child.stderr.on('data', (c) => { err += c }) + child.on('error', reject) + child.on('close', (code) => { + if (code === 0) resolve() + else reject(new Error(`npm install failed (${code}): ${err.slice(-600)}`)) + }) }) - }) + } installed = true onProgress?.('install', 1) } @@ -420,7 +432,9 @@ async function createFragment(spec: FragmentSpec, projectDir?: string | null): P name: spec.name, width: Math.round(spec.width), height: Math.round(spec.height), - fps: Math.max(60, Math.round(spec.fps)), + // honor the requested (project) fps — forcing ≥60 doubled the PNG frame + // pile on tmpfs for 30 fps projects (see renderFragment TMPDIR handling) + fps: Math.max(1, Math.round(spec.fps)), durationInFrames: Math.max(1, Math.round(spec.durationInFrames)), transparent: !!spec.transparent } @@ -516,15 +530,47 @@ function fragmentHash(id: string): string { const renderDir = () => join(app.getPath('userData'), 'fragment-renders') let renderChain: Promise = Promise.resolve() +/** Fail fast (with a clear message) if the temp filesystem can't hold the PNG + * frame pile Remotion produces. ~0.25 B/px/frame is conservative vs the + * ~0.18 B/px measured on flat fills. Best-effort: skips if it can't estimate. */ +async function ensureRenderSpace( + dir: string, + meta: { width?: number; height?: number; durationInFrames?: number } +): Promise { + const { width, height, durationInFrames } = meta + if (!width || !height || !durationInFrames) return + let free: number + try { + const st = await fs.statfs(dir) + free = st.bavail * st.bsize + } catch { + return // statfs unavailable — skip the guard rather than block the render + } + const est = width * height * 0.25 * durationInFrames + if (free < est) { + const gb = (n: number) => (n / 1e9).toFixed(2) + throw new Error( + `Not enough disk for this fragment render: ~${gb(est)} GB of temp frames ` + + `(${durationInFrames} × ${width}×${height} PNG) but only ${gb(free)} GB free ` + + `at ${dir}. Free up space, or lower the fragment resolution/fps/length.` + ) + } +} + async function renderFragment( id: string, opts: { transparent?: boolean } | undefined, onProgress: (p: number) => void ): Promise<{ path: string; cached: boolean }> { await ensureWorkspace() + // Heal the registry against the folders actually on disk BEFORE bundling: a + // fragment folder deleted outside the app (rm -rf) would otherwise leave a + // stale `import … from './'` in src/fragments/index.ts and fail the render. + await regenRegistry() let transparent = !!opts?.transparent + let meta: { width?: number; height?: number; durationInFrames?: number; transparent?: boolean } = {} try { - const meta = JSON.parse(await fs.readFile(join(FRAG_DIR(), id, 'meta.json'), 'utf8')) + meta = JSON.parse(await fs.readFile(join(FRAG_DIR(), id, 'meta.json'), 'utf8')) if (opts?.transparent === undefined) transparent = !!meta.transparent } catch { /* meta is optional for the decision */ } const ext = transparent ? 'webm' : 'mp4' @@ -555,6 +601,15 @@ async function renderFragment( if (transparent) args.push('--codec=vp9', '--pixel-format=yuva420p', '--crf=12') else args.push('--codec=h264', '--crf=15') + // Remotion writes every frame as a full-res PNG to the OS temp dir before + // muxing. On this machine /tmp is a small tmpfs (~1.8 GB free) and a 4K + // fragment overflowed it mid-render (EDQUOT at ~90%). Redirect Remotion's + // temp to a disk-backed dir under userData, and pre-flight the space so we + // fail up front with a clear message instead of after a 10-minute render. + const tmpDir = join(app.getPath('userData'), 'fragment-tmp') + await fs.mkdir(tmpDir, { recursive: true }) + await ensureRenderSpace(tmpDir, meta) + const extraEnv = await netEnv() const job = renderChain.then(async () => { try { @@ -565,7 +620,8 @@ async function renderFragment( await new Promise((resolve, reject) => { const child = spawn('npx', args, { cwd: WORKSPACE, - env: { ...process.env, ...extraEnv }, + // TMPDIR: keep Remotion's per-frame PNGs off the tmpfs (see above) + env: { ...process.env, ...extraEnv, TMPDIR: tmpDir }, stdio: ['ignore', 'pipe', 'pipe'] }) let all = '' diff --git a/electron/gpu.ts b/electron/gpu.ts new file mode 100644 index 0000000..ff72088 --- /dev/null +++ b/electron/gpu.ts @@ -0,0 +1,195 @@ +// GPU selection. Which physical GPU Chromium's GPU process uses is decided +// ONCE at startup (before app 'ready'), so the user's choice is persisted to +// userData/gpu.json and re-applied here on every launch; changing it needs an +// app relaunch. On this Optimus-style laptop the default is the integrated +// Intel iGPU — forcing the discrete GPU speeds up the WebGL compositor (preview +// + the offline export composite), though the final H.264 encode stays on x264. +// +// SAFETY: a bad choice (e.g. NVIDIA that won't come up under this compositor) +// must never brick the app. A newly-picked GPU is applied as a 'trial': the +// file is flipped to 'failed' BEFORE the window is created, and only flipped to +// 'ok' once the renderer actually finishes loading (confirmGpuTrial, wired to +// did-finish-load). So if the trial launch shows no window, the NEXT launch +// sees 'failed' and reverts to auto — the app heals itself. +import { app } from 'electron' +import { readFileSync, writeFileSync } from 'fs' +import { readdirSync, openSync } from 'fs' +import { spawn } from 'child_process' +import { join } from 'path' +import type { GpuInfo } from '@shared/types' + +const VENDOR_NAME: Record = { + '0x10de': 'NVIDIA', + '0x8086': 'Intel', + '0x1002': 'AMD', + '0x1022': 'AMD' +} + +export type GpuStatus = 'trial' | 'ok' | 'failed' +export interface GpuChoice { + node?: string // /dev/dri/renderD129 ; absent/empty = auto (no override) + status?: GpuStatus +} + +// same file the renderer writes via writeUserStore('gpu', …) — see main's +// userStorePath(): userData/.json +const gpuStorePath = () => join(app.getPath('userData'), 'gpu.json') + +export function readGpuChoice(): GpuChoice { + try { + return JSON.parse(readFileSync(gpuStorePath(), 'utf8')) as GpuChoice + } catch { + return {} + } +} + +function writeGpuChoice(c: GpuChoice): void { + try { + writeFileSync(gpuStorePath(), JSON.stringify(c, null, 1)) + } catch { + /* best effort; a read failure next launch just falls back to auto */ + } +} + +/** Enumerate DRM render nodes into user-choosable GPUs (Linux only). */ +export function enumerateGpus(): GpuInfo[] { + let nodes: string[] + try { + nodes = readdirSync('/dev/dri').filter((n) => /^renderD\d+$/.test(n)) + } catch { + return [] + } + const read = (base: string, f: string) => { + try { + return readFileSync(join(base, f), 'utf8').trim() + } catch { + return '' + } + } + return nodes.sort().map((n) => { + const base = `/sys/class/drm/${n}/device` + const vendorId = read(base, 'vendor') + const deviceId = read(base, 'device') + const driver = read(base, 'uevent').match(/DRIVER=(\S+)/)?.[1] ?? '' + // boot_vga=1 marks the primary display adapter — the integrated GPU on + // laptops. Absent (0/missing) plus a discrete driver ⇒ the dGPU. + const integrated = read(base, 'boot_vga') === '1' || driver === 'i915' + return { + node: `/dev/dri/${n}`, + driver, + vendorId, + deviceId, + vendor: VENDOR_NAME[vendorId] ?? driver ?? 'GPU', + integrated + } + }) +} + +// set when THIS launch applied a not-yet-proven GPU; confirmGpuTrial() promotes +// it to 'ok' only after the user confirms the window renders. +let trialNode: string | null = null + +const NVIDIA_ENV = { + __NV_PRIME_RENDER_OFFLOAD: '1', + __GLX_VENDOR_LIBRARY_NAME: 'nvidia', + __VK_LAYER_NV_optimus: 'NVIDIA_only' +} + +/** + * Re-exec the app on the NVIDIA GPU via PRIME render offload — the way Lutris + * launches games: an ordinary resizable window (no gamescope), the dGPU renders + * and the iGPU-driven display presents. The offload vars MUST be in the + * environment from process START: Chromium forks its zygote/GPU process very + * early, and setting the vars late in main leaves the GPU process on the iGPU + * render node while GLX is forced to NVIDIA — that mismatch segfaults it + * (exit_code=139, no window). So relaunch with the vars in the child's env, then + * exit. ELECTRON_OZONE_PLATFORM_HINT=x11 routes presentation through XWayland/GLX + * where the offload actually takes effect (native Wayland leaves EGL on the iGPU). + */ +function relaunchWithOffload(): void { + const env: NodeJS.ProcessEnv = { + ...process.env, + ...NVIDIA_ENV, + KADR_GPU_OFFLOAD: '1' + } + // load the built renderer (out/), not a dev-server URL that dies on relaunch + delete env.ELECTRON_RENDERER_URL + let stdio: 'ignore' | ['ignore', number, number] = 'ignore' + try { + // userData is per-user (not world-writable /tmp) — no symlink-attack; kept + // for diagnosing GPU/renderer failures on the detached process + const fd = openSync(join(app.getPath('userData'), 'gpu.log'), 'a') + stdio = ['ignore', fd, fd] + } catch { /* fall back to ignore */ } + // --ozone-platform=x11 as a CLI flag: the ELECTRON_OZONE_PLATFORM_HINT env is + // NOT honored on a Wayland session (Chromium stays on wayland, where NVIDIA + // is Vulkan-incompatible and falls back to software). XWayland/GLX is where + // the PRIME offload actually renders on the NVIDIA GPU. + const args = [ + ...process.argv.slice(1).filter((a) => !a.startsWith('--ozone-platform')), + '--ozone-platform=x11' + ] + spawn(process.execPath, args, { env, detached: true, stdio }).unref() + app.exit(0) +} + +function applyGpu(gpu: GpuInfo): void { + if (gpu.driver === 'nvidia') { + relaunchWithOffload() // exits and re-execs with the offload env from start + return + } + // Intel / AMD: point Chromium's GPU process straight at the render node. + app.commandLine.appendSwitch('render-node-override', gpu.node) + process.env.KADR_GPU_POWER = gpu.integrated ? 'low-power' : 'high-performance' +} + +/** + * Read the persisted choice and, before app 'ready', route rendering to it. + * Must run at main module load. A 'trial' is consumed (→ 'failed') before use, + * so a launch that never renders heals back to auto next time. + */ +export function applyGpuChoiceAtStartup(): void { + process.env.KADR_GPU_POWER = 'default' + // re-exec'd with the PRIME offload env already in the environment (from process + // start) — Chromium is on NVIDIA; nothing to select. powerPreference stays + // 'default' to match the working manual `__NV_PRIME… electron .` run. + if (process.env.KADR_GPU_OFFLOAD) return + + const choice = readGpuChoice() + if (!choice.node) return + const gpu = enumerateGpus().find((g) => g.node === choice.node) + if (!gpu) return + + if (choice.status === 'ok') { + applyGpu(gpu) // proven-good on a previous launch + } else if (choice.status === 'trial') { + writeGpuChoice({ node: gpu.node, status: 'failed' }) // heal to auto if it never renders + trialNode = gpu.node + applyGpu(gpu) + } + // 'failed' or anything else → leave at auto (the self-heal path) +} + +/** Promote a surviving trial to 'ok' (reachable only when the window renders — + * the Settings "Keep" button). The offload re-exec sets trialNode in the outer + * process which then exits, so fall back to the on-disk choice. */ +export function confirmGpuTrial(): void { + const node = trialNode ?? readGpuChoice().node + if (!node) return + writeGpuChoice({ node, status: 'ok' }) + trialNode = null +} + +/** Env for the fresh process a GPU switch relaunches into: clears the NVIDIA + * offload vars / x11 hint (so switching back to Intel goes native Wayland on the + * iGPU) and ELECTRON_RENDERER_URL (a switch may relaunch from a now-dead dev + * server — load the built out/ instead). */ +export function cleanRelaunchEnv(): NodeJS.ProcessEnv { + const env = { ...process.env } + for (const k of [ + 'KADR_GPU_OFFLOAD', 'ELECTRON_OZONE_PLATFORM_HINT', + '__NV_PRIME_RENDER_OFFLOAD', '__GLX_VENDOR_LIBRARY_NAME', '__VK_LAYER_NV_optimus', + 'ELECTRON_RENDERER_URL' + ]) delete env[k] + return env +} diff --git a/electron/main.ts b/electron/main.ts index 6457b5d..8719437 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -1,9 +1,12 @@ +// Side-effect import — MUST stay first so bundled-runtime PATH/env is set +// before './ffmpeg' (which captures KADR_FFMPEG/FFPROBE at load) is imported. +import './runtime-env' import { app, BrowserWindow, ipcMain, dialog, protocol, net, clipboard } from 'electron' import { join, dirname, basename } from 'path' import { promises as fs, createReadStream, statSync, existsSync, appendFileSync } from 'fs' import { tmpdir } from 'os' import { createHash, randomBytes } from 'crypto' -import { execFile } from 'child_process' +import { execFile, spawn, spawnSync } from 'child_process' import { probeMedia, makeProxy, makeDecoded, makeReversed, measureLoudness, packedAlphaPlan, ExportMuxer, RawVideoEncoder, meanVolume @@ -16,6 +19,7 @@ import { registerTtsIpc } from './tts' import { registerVoiceIpc } from './voice' import { registerEnvelopeIpc } from './envelope' import { registerFragmentIpc } from './fragments' +import { enumerateGpus, applyGpuChoiceAtStartup, confirmGpuTrial, cleanRelaunchEnv } from './gpu' import type { ExportJob, Project } from '@shared/types' // Streamed local media under a privileged scheme so the renderer can play @@ -54,12 +58,26 @@ app.commandLine.appendSwitch('password-store', 'basic') // Let Chromium use VAAPI for hardware video encode/decode where the driver // allows it (Intel iGPU on this machine); WebCodecs then picks it up via // hardwareAcceleration: 'prefer-hardware'. +// +// EXCEPT on the NVIDIA PRIME-offload path (KADR_GPU_OFFLOAD): there is no VA +// driver for the NVIDIA GLX context, so VaapiVideoDecoder init fails ("Could +// not get a valid VA display") and — worse — a WebCodecs decode can then wedge +// with neither an output nor an error callback, hanging the offline export +// forever. So on that path we DON'T enable VAAPI decode (WebCodecs falls back +// to software decode, which works); GPU compositing still runs on the dGPU. app.commandLine.appendSwitch('ignore-gpu-blocklist') app.commandLine.appendSwitch( 'enable-features', - 'VaapiVideoEncoder,VaapiVideoDecoder,VaapiVideoDecodeLinuxGL,AcceleratedVideoEncoder' + process.env.KADR_GPU_OFFLOAD + ? 'VaapiVideoEncoder,AcceleratedVideoEncoder' + : 'VaapiVideoEncoder,VaapiVideoDecoder,VaapiVideoDecodeLinuxGL,AcceleratedVideoEncoder' ) +// Point the GPU process at the user's chosen render node (else Chromium's +// default = the integrated GPU). MUST run before app 'ready'. Also sets +// KADR_GPU_POWER for the renderer's WebGL powerPreference. +applyGpuChoiceAtStartup() + // Last line of defense: a stray async error (e.g. a stream racing a request // abort) must be logged, not shown as a modal error dialog over the editor. process.on('uncaughtException', (err) => { @@ -253,7 +271,16 @@ app.whenReady().then(() => { registerEnvelopeIpc() registerStorageIpc() registerFragmentIpc(() => win) + void sweepExportTemps() // reclaim multi-GB export intermediates left by a crash createWindow() + // log which GPU Chromium actually settled on — the diagnostic for the GPU + // picker (0x8086 = Intel, 0x10de = NVIDIA, 0x1002 = AMD) + app.getGPUInfo('basic').then((info) => { + const dev = (info as { gpuDevice?: Array<{ active?: boolean; vendorId?: number; deviceId?: number }> }).gpuDevice + const g = dev?.find((d) => d.active) ?? dev?.[0] + const hex = (n?: number) => (n == null ? '?' : '0x' + n.toString(16)) + console.log(`[kadr] active GPU: vendor=${hex(g?.vendorId)} device=${hex(g?.deviceId)}`) + }).catch(() => { /* getGPUInfo unavailable — Settings readout still shows it */ }) app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) @@ -264,8 +291,10 @@ app.on('window-all-closed', () => { app.quit() // an in-flight export/muxer or any stray handle must never keep a // windowless process alive — a lingering instance blocks the next - // launch and reads as "the editor won't open anymore" - setTimeout(() => app.exit(0), 2500) + // launch and reads as "the editor won't open anymore". On a clean close + // force-exit quickly: a slow GPU-process teardown (NVIDIA PRIME offload) + // otherwise makes closing hang for seconds. Give an export longer to flush. + setTimeout(() => app.exit(0), exportState ? 2500 : 400) } }) @@ -313,11 +342,16 @@ let proxyChain: Promise = Promise.resolve() async function requestProxy( srcPath: string, duration: number, - opts?: { alpha?: boolean; codec?: string } + opts?: { alpha?: boolean; codec?: string; audioOnly?: boolean } ): Promise { const stat = statSync(srcPath) - // alpha proxies are a different artifact (webm) — separate cache identity - const key = mediaCacheKey(srcPath, stat.size, stat.mtimeMs, proxySuffix(opts?.alpha)) + // alpha proxies are a different artifact (webm), and an audio-only proxy + // (a music/voice file with an undecodable codec) yet another — each needs + // its own cache identity + const key = mediaCacheKey( + srcPath, stat.size, stat.mtimeMs, + proxySuffix(opts?.alpha) + (opts?.audioOnly ? '-a' : '') + ) const ext = opts?.alpha ? 'webm' : 'mp4' const out = join(proxyDir(), `${key}.${ext}`) try { @@ -542,9 +576,33 @@ async function rememberDir(kind: string, filePath: string) { } catch { /* best effort */ } } +// Is NVIDIA hardware H.264 encoding actually usable? Listing h264_nvenc in +// -encoders is NOT enough: ffmpeg's bundled NVENC SDK can be newer than the +// installed driver (e.g. build needs NVENC API 13.1 but the driver only has +// 13.0), and it fails only at encode time. So we run a real 1-frame encode and +// trust the exit code — a mismatch just leaves the NVENC option off and x264 is +// used, instead of a hard export error. Probed once, cached. +let nvencCache: boolean | null = null +function nvencAvailable(): boolean { + if (nvencCache !== null) return nvencCache + try { + if (!existsSync('/dev/nvidia0')) return (nvencCache = false) + const ff = process.env.KADR_FFMPEG || 'ffmpeg' + const r = spawnSync(ff, [ + '-hide_banner', '-v', 'error', + '-f', 'lavfi', '-i', 'color=c=black:s=64x64:d=1', + '-frames:v', '1', '-c:v', 'h264_nvenc', '-f', 'null', '-' + ], { timeout: 15000 }) + nvencCache = r.status === 0 + } catch { + nvencCache = false + } + return nvencCache +} + function registerIpc() { ipcMain.handle('proxy:request', (_e, srcPath: string, duration: number, - opts?: { alpha?: boolean; codec?: string }) => + opts?: { alpha?: boolean; codec?: string; audioOnly?: boolean }) => requestProxy(srcPath, duration, opts) ) @@ -580,6 +638,36 @@ function registerIpc() { await fs.writeFile(userStorePath(name), JSON.stringify(data, null, 1)) }) + ipcMain.handle('nvenc:available', () => nvencAvailable()) + ipcMain.handle('gpu:list', () => enumerateGpus()) + // called from Settings only when the user can SEE the switched GPU works — + // a blank window can't confirm, so it heals to auto on the next launch + ipcMain.handle('gpu:confirm', () => confirmGpuTrial()) + ipcMain.handle('gpu:relaunch', () => { + // spawn a fresh top-level process (not app.relaunch) so cleanRelaunchEnv can + // clear the NVIDIA offload vars / x11 hint and a dev-server URL — the new + // process re-reads gpu.json and applies the chosen GPU from scratch. + // drop any --ozone-platform=x11 the NVIDIA path added, so a switch back to + // Intel runs native Wayland; the new process re-adds x11 only if it picks NVIDIA + const relaunchArgs = process.argv.slice(1).filter((a) => !a.startsWith('--ozone-platform')) + // KNOWN, dev-only: a relaunch loses --remote-debugging-port. Chromium's + // DevTools listening socket has no CLOEXEC, so the child inherits the fd; + // its own bind then fails with EADDRINUSE against itself, no DevTools + // server starts (DevToolsActivePort is never rewritten), and the socket + // sits in LISTEN with nobody accepting — Recv-Q climbs to the backlog and + // every CDP client hangs. Measured: the socket inode is IDENTICAL either + // side of a switch, so it is the same socket, not a re-bind. Waiting for + // this process to exit first does NOT help — the fd is duplicated at spawn, + // long before any wait. Only affects runs started with a debugging port + // (e2e, automation); relaunch from the terminal to get one back. + spawn(process.execPath, relaunchArgs, { + env: cleanRelaunchEnv(), + detached: true, + stdio: 'ignore' + }).unref() + app.exit(0) + }) + ipcMain.handle('media:open-dialog', async () => { const r = await dialog.showOpenDialog(win!, { properties: ['openFile', 'multiSelections'], @@ -593,6 +681,16 @@ function registerIpc() { ipcMain.on('media:token', (e) => { e.returnValue = MEDIA_TOKEN }) ipcMain.handle('media:probe', (_e, path: string) => probeMedia(path)) + // cheap freshness check for the import dedupe — a re-import of a file + // overwritten with the same name should refresh, not reuse the stale asset + ipcMain.handle('media:stat', (_e, path: string) => { + try { + const s = statSync(path) + return { mtimeMs: s.mtimeMs, size: s.size } + } catch { + return null + } + }) // sanitized basename + MIME-derived extension for downloaded/pasted media const mediaBase = (name: string, mime: string): string => { @@ -808,7 +906,9 @@ function registerIpc() { ipcMain.handle('export:begin', async (_e, job: ExportJob) => { await cleanupExport() - const videoTemp = join(tmpdir(), `kadr-export-${Date.now()}.mp4`) + // disk-backed temp (KADR_TMPDIR) — os.tmpdir() is often a small tmpfs (see + // runtime-env.ts); a multi-GB export intermediate overflows it otherwise + const videoTemp = join(process.env.KADR_TMPDIR || tmpdir(), `kadr-export-${Date.now()}.mp4`) const fh = job.preset.audioOnly ? null : await fs.open(videoTemp, 'w') exportState = { job, videoTemp, fh, muxer: null, raw: null, rawEncoded: false, @@ -938,3 +1038,26 @@ async function cleanupExport() { try { await st.fh?.close() } catch { /* already closed */ } try { await fs.unlink(st.videoTemp) } catch { /* never created */ } } + +// Delete raw-export / mux temp files (kadr-export[-raw]-.mp4) orphaned by a +// previous crash or hard kill — these intermediates can be multi-GB. Only files +// older than a minute are touched, so a concurrent instance's live export (temps +// named by Date.now()) is never removed. Covers KADR_TMPDIR and os.tmpdir() +// (leftovers from before temps moved off the tmpfs). +async function sweepExportTemps() { + const cutoff = Date.now() - 60_000 + const dirs = new Set([process.env.KADR_TMPDIR, tmpdir()].filter(Boolean) as string[]) + for (const dir of dirs) { + let names: string[] + try { + names = await fs.readdir(dir) + } catch { continue } // dir missing — nothing to sweep + for (const name of names) { + if (!/^kadr-export(-raw)?-\d+\.mp4$/.test(name)) continue + const p = join(dir, name) + try { + if ((await fs.stat(p)).mtimeMs < cutoff) await fs.unlink(p) + } catch { /* raced with another cleanup */ } + } + } +} diff --git a/electron/preload.ts b/electron/preload.ts index 75e7542..4517a7d 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -20,13 +20,21 @@ const MEDIA_TOKEN: string = ipcRenderer.sendSync('media:token') const api: KadrApi = { rawEncodeStart: (o) => { - const out = join(tmpdir(), `kadr-export-raw-${Date.now()}.mp4`) + // disk-backed temp (KADR_TMPDIR) — os.tmpdir() is often a small tmpfs that a + // big export overflows mid-encode (see runtime-env.ts) + const out = join(process.env.KADR_TMPDIR || tmpdir(), `kadr-export-raw-${Date.now()}.mp4`) const child = spawn(process.env.KADR_FFMPEG || 'ffmpeg', rawEncodeArgs({ ...o, out }), { stdio: ['pipe', 'ignore', 'pipe'] }) rawEnc = child rawEncErr = '' child.stderr!.on('data', (c) => { rawEncErr += c }) + // Killing the encoder (a cancelled or aborted export) breaks this pipe + // mid-write, and a stdin stream with no 'error' listener turns that EPIPE + // into an uncaught exception — three "сбой" rows in the session log every + // time someone cancels an export. The failure is already delivered where it + // can be acted on: the write callback below, and rawEncExit. + child.stdin!.on('error', () => { /* see rawEncodeFrame / rawEncExit */ }) rawEncExit = new Promise((resolve, reject) => { child.on('close', (code) => { rawEnc = null @@ -73,6 +81,7 @@ const api: KadrApi = { openMediaDialog: () => ipcRenderer.invoke('media:open-dialog'), probeMedia: (path) => ipcRenderer.invoke('media:probe', path), + statMedia: (path) => ipcRenderer.invoke('media:stat', path), fileUrl: (path) => { // Windows paths (D:\dir\file) must become /D:/dir/file — a raw drive // letter glued after the host ('kadr://mediaD:\…') is an INVALID URL: @@ -161,6 +170,7 @@ const api: KadrApi = { return () => ipcRenderer.removeListener('fragment:progress', handler) }, + defaultWhisperModel: process.env.KADR_WHISPER_MODEL || '', ttsSpeakPhrase: (req) => ipcRenderer.invoke('tts:speak-phrase', req), voiceSplice: (req) => ipcRenderer.invoke('voice:splice', req), voiceSilenceAt: (path, at) => ipcRenderer.invoke('voice:silence-at', path, at), @@ -214,7 +224,13 @@ const api: KadrApi = { const handler = (_e: unknown, code: number) => cb(code) ipcRenderer.on('claude:exit', handler) return () => ipcRenderer.removeListener('claude:exit', handler) - } + }, + + defaultGpuPower: process.env.KADR_GPU_POWER || '', + nvencAvailable: () => ipcRenderer.invoke('nvenc:available'), + gpuList: () => ipcRenderer.invoke('gpu:list'), + gpuConfirm: () => { void ipcRenderer.invoke('gpu:confirm') }, + relaunchApp: () => { void ipcRenderer.invoke('gpu:relaunch') } } // contextIsolation is off (see main.ts: export frames pass by reference), diff --git a/electron/runtime-env.ts b/electron/runtime-env.ts new file mode 100644 index 0000000..0a09047 --- /dev/null +++ b/electron/runtime-env.ts @@ -0,0 +1,75 @@ +// Bundled-runtime wiring for packaged builds. MUST be imported before any +// module that reads KADR_FFMPEG/KADR_FFPROBE or resolves a spawned binary from +// PATH at load time — notably `./ffmpeg` (its FFMPEG/FFPROBE consts are captured +// at import). Keep this the very first import in electron/main.ts. +// +// The self-contained archive ships ffmpeg/ffprobe/node/npm/npx/python3 under +// resources/runtime/bin. Prepending that dir to PATH makes every literal spawn +// (`python3` in transcribe.ts, `npm`/`npx` in fragments.ts, `node` for the MCP +// bridge) resolve to the bundled copy first, and pointing KADR_FFMPEG/FFPROBE at +// it covers both the main-process ffmpeg.ts and the renderer's preload encoder +// (the renderer inherits this env when its process is spawned). In dev +// (`app.isPackaged` false) nothing changes — the host toolchain is used as before. +import { app } from 'electron' +import { readFileSync, mkdirSync } from 'fs' +import { homedir } from 'os' +import { join } from 'path' + +// Register with PipeWire/PulseAudio as "Kadr" instead of Chromium's default +// "Chromium" (Electron reports the Chromium product name). Beyond looking right +// in the mixer, this gives Kadr its OWN per-app volume: otherwise it shares the +// stream-restore entry of everything named "Chromium", so a remembered 0% there +// silences Kadr's output even though its audio graph runs. OVERRIDE (REPLACE) +// forces over Chromium's own proplist value; PULSE_PROP is a belt-and-braces +// fallback. Set for dev too, and before any window so the audio service — a +// later child process — inherits it. `??=` leaves a user-set value untouched. +// (Only application.name is settable this way — it's a client/context property. +// application.icon_name is stamped by Chromium on the stream itself and can't be +// overridden via env, so the mixer icon stays Chromium's; the Kadr logo still +// shows in the app menu, task bar and window from the packaged icon.) +process.env.PULSE_PROP_OVERRIDE ??= 'application.name=Kadr' +process.env.PULSE_PROP ??= 'application.name=Kadr' + +// Export/render intermediates (the raw-video encode and the mux temp) can be +// multi-GB. os.tmpdir() is a small tmpfs on many Linux setups — here /tmp is a +// 7.7 GB RAM-backed tmpfs — so a big export fills it mid-run and ffmpeg dies with +// EDQUOT/ENOSPC ("Disk quota exceeded", errno -122). Route the temps to a +// disk-backed dir under userData instead; preload.ts and main.ts read +// KADR_TMPDIR (falling back to os.tmpdir()), and the renderer inherits this env +// when its process is spawned. Set for dev too — same tmpfs trap there. +try { + const t = join(app.getPath('userData'), 'tmp') + mkdirSync(t, { recursive: true }) + process.env.KADR_TMPDIR ??= t +} catch { /* fall back to os.tmpdir() at the call sites */ } + +if (app.isPackaged) { + const runtime = join(process.resourcesPath, 'runtime') + const bin = join(runtime, 'bin') + // Desktop/menu launches (e.g. an /opt copy started from the app menu) inherit + // a minimal PATH that usually omits the user's own bin dirs, so a + // user-installed `claude` CLI (the embedded AI panel) can't be found. Prepend + // the bundled runtime (our ffmpeg must win); append the common user locations + // so `claude` resolves however Kadr was launched. Missing dirs are harmless. + const home = homedir() + const userBins = [ + join(home, '.local', 'bin'), + join(home, 'bin'), + join(home, '.npm-global', 'bin'), + join(home, '.claude', 'local'), + '/home/linuxbrew/.linuxbrew/bin', + '/usr/local/bin' + ] + process.env.PATH = [bin, process.env.PATH ?? '', ...userBins].filter(Boolean).join(':') + process.env.KADR_FFMPEG ??= join(bin, 'ffmpeg') + process.env.KADR_FFPROBE ??= join(bin, 'ffprobe') + // Name of the whisper model pre-bundled by `pack.sh --model `. The + // renderer reads this (via preload) to default subtitles to the model that + // was actually shipped, and transcribe.ts seeds it into the writable HF cache + // (~/.cache/huggingface) on first use — so it's found, not re-downloaded, and + // the read-only /opt copy is never written to. Absent → normal on-demand DL. + try { + const m = readFileSync(join(runtime, 'whisper-model.txt'), 'utf8').trim() + if (m) process.env.KADR_WHISPER_MODEL = m + } catch { /* no model bundled */ } +} diff --git a/electron/transcribe.ts b/electron/transcribe.ts index 531d83e..10d0c8f 100644 --- a/electron/transcribe.ts +++ b/electron/transcribe.ts @@ -4,18 +4,39 @@ // text to the renderer. One job at a time. import { app, ipcMain, BrowserWindow } from 'electron' import { spawn, ChildProcess } from 'child_process' -import { promises as fs } from 'fs' +import { promises as fs, existsSync } from 'fs' import { join } from 'path' -import { tmpdir } from 'os' +import { tmpdir, homedir } from 'os' import { ExportMuxer, mixdownWav } from './ffmpeg' import type { TranscribeRequest, TranscribeResult, TranscribeSegment } from '@shared/types' let current: { muxer: ExportMuxer | null; py: ChildProcess | null; cancelled: boolean } | null = null +// A whisper model bundled by `pack.sh --model` (resources/runtime/whisper-seed) +// is copied into the user's writable HF cache on first use, so faster-whisper +// finds it there instead of downloading — and the read-only /opt copy is never +// written to. One-time; dev or no-bundle → no-op, falling back to on-demand DL. +let seededModels = false +async function seedBundledModels(): Promise { + if (seededModels || !app.isPackaged) return + seededModels = true + try { + const srcHub = join(process.resourcesPath, 'runtime', 'whisper-seed', 'hub') + if (!existsSync(srcHub)) return + const dstHub = join(homedir(), '.cache', 'huggingface', 'hub') + await fs.mkdir(dstHub, { recursive: true }) + for (const name of await fs.readdir(srcHub)) { + if (!name.startsWith('models--') || existsSync(join(dstHub, name))) continue + await fs.cp(join(srcHub, name), join(dstHub, name), { recursive: true }) + } + } catch { /* fall back to on-demand download */ } +} + async function run(win: BrowserWindow, req: TranscribeRequest): Promise { if (current) throw new Error('transcription already running') const job = { muxer: null as ExportMuxer | null, py: null as ChildProcess | null, cancelled: false } current = job + await seedBundledModels() const wav = join(tmpdir(), `kadr-transcribe-${Date.now()}.wav`) const send = (progress: number, text: string) => win.webContents.send('transcribe:progress', { progress, text }) diff --git a/package-lock.json b/package-lock.json index 1fe0513..996a7ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,20 @@ { "name": "kadr", - "version": "0.1.0", + "version": "0.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "kadr", - "version": "0.1.0", + "version": "0.2.1", "hasInstallScript": true, "license": "GPL-3.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.29.0", "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", - "mp4-muxer": "^5.1.5", + "mediabunny": "^1.50.8", + "mp4-muxer": "^5.2.2", "mp4box": "^2.3.0", "node-pty": "^1.1.0", "react": "^18.3.1", @@ -30,6 +31,7 @@ "@types/ws": "^8.18.1", "@vitejs/plugin-react": "^5.2.0", "electron": "^42.4.0", + "electron-builder": "^26.0.12", "electron-vite": "^5.0.0", "typescript": "^5.5.4", "vite": "^7.3.1" @@ -343,6 +345,86 @@ "node": ">=22.12.0" } }, + "node_modules/@electron/asar": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", + "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/asar/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@electron/fuses": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", + "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.1", + "fs-extra": "^9.0.1", + "minimist": "^1.2.5" + }, + "bin": { + "electron-fuses": "dist/bin.js" + } + }, + "node_modules/@electron/fuses/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@electron/get": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.0.0.tgz", @@ -390,6 +472,72 @@ "node": ">=10" } }, + "node_modules/@electron/notarize": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz", + "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, "node_modules/@electron/rebuild": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.0.4.tgz", @@ -411,6 +559,112 @@ "node": ">=22.12.0" } }, + "node_modules/@electron/universal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.3.tgz", + "integrity": "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.3.1", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=16.4" + } + }, + "node_modules/@electron/universal/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.6.tgz", + "integrity": "sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", + "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/windows-sign/node_modules/fs-extra": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.6.tgz", + "integrity": "sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -951,6 +1205,38 @@ "node": ">= 12.13.0" } }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", @@ -991,6 +1277,71 @@ } } }, + "node_modules/@noble/hashes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz", + "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.8.0.tgz", + "integrity": "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/json-schema": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", + "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@peculiar/utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", + "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/webcrypto": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.7.1.tgz", + "integrity": "sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "tslib": "^2.8.1", + "webcrypto-core": "^1.9.2" + }, + "engines": { + "node": ">=14.18.0" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.3", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", @@ -1348,6 +1699,32 @@ "win32" ] }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1393,10 +1770,42 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/dom-mediacapture-transform": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@types/dom-mediacapture-transform/-/dom-mediacapture-transform-0.1.12.tgz", + "integrity": "sha512-d7/QsLRwF864A5mgIM/YrfiglHoYn7zgCcAoJgW404r+2DwnNr7EBbLnCWpmOMgH8y0te73L1AV6H1bmauaWFw==", + "license": "MIT", + "dependencies": { + "@types/dom-webcodecs": "*" + } + }, "node_modules/@types/dom-webcodecs": { - "version": "0.1.18", - "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.18.tgz", - "integrity": "sha512-vAvE8C9DGWR+tkb19xyjk1TSUlJ7RUzzp4a9Anu7mwBT+fpyePWK1UxmH14tMO5zHmrnrRIMg5NutnnDztLxgg==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz", + "integrity": "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==", "license": "MIT" }, "node_modules/@types/estree": { @@ -1406,6 +1815,40 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.19.42", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.42.tgz", @@ -1444,6 +1887,16 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/wicg-file-system-access": { "version": "2020.9.8", "resolved": "https://registry.npmjs.org/@types/wicg-file-system-access/-/wicg-file-system-access-2020.9.8.tgz", @@ -1481,6 +1934,16 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@xterm/addon-fit": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.11.0.tgz", @@ -1528,6 +1991,16 @@ "node": ">= 0.6" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", @@ -1561,19 +2034,325 @@ } } }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.35", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.35.tgz", - "integrity": "sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/app-builder-lib": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.15.3.tgz", + "integrity": "sha512-2VnyWkqsP5v5XbBhL3tD5Syx8iNPBYsoU7kY4S2fz7wg8Rj/nztWKCUzGKaFRTv0Xwf3/H058CR1Kvtd/3lRow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "3.4.1", + "@electron/fuses": "^1.8.0", + "@electron/get": "^3.0.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", + "@noble/hashes": "^2.2.0", + "@peculiar/webcrypto": "^1.7.1", + "@types/fs-extra": "9.0.13", + "ajv": "^8.18.0", + "asn1js": "^3.0.10", + "async-exit-hook": "^2.0.1", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", + "chromium-pickle-js": "^0.2.0", + "ci-info": "4.3.1", + "debug": "^4.3.4", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "ejs": "^3.1.8", + "electron-publish": "26.15.3", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "isbinaryfile": "^5.0.0", + "jiti": "^2.4.2", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "lazy-val": "^1.0.5", + "minimatch": "^10.2.5", + "pkijs": "^3.4.0", + "plist": "3.1.0", + "proper-lockfile": "^4.1.2", + "resedit": "^1.7.0", + "semver": "~7.7.3", + "tar": "^7.5.7", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0", + "unzipper": "^0.12.3", + "which": "^5.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "26.15.3", + "electron-builder-squirrel-windows": "26.15.3" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-3.1.0.tgz", + "integrity": "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=14" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/app-builder-lib/node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/app-builder-lib/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asn1js": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", + "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.5", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.35", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.35.tgz", + "integrity": "sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, "engines": { "node": ">=6.0.0" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, "node_modules/body-parser": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", @@ -1614,6 +2393,28 @@ "url": "https://opencollective.com/express" } }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/browserslist": { "version": "4.28.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", @@ -1648,6 +2449,53 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/builder-util": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.15.3.tgz", + "integrity": "sha512-q2hn7Mbo2nFNkVekPiHFx6Nfo3hURmES3tfBn+k5Pqxl2RkmP3QGqZUhH/q9Pch/4G05NRhPjDlVj1O8q4Txvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.6", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "js-yaml": "^4.1.0", + "sanitize-filename": "^1.6.3", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0", + "tiny-async-pool": "1.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.7.0.tgz", + "integrity": "sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1657,6 +2505,16 @@ "node": ">= 0.8" } }, + "node_modules/bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -1667,6 +2525,35 @@ "node": ">=8" } }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -1717,6 +2604,23 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -1727,6 +2631,117 @@ "node": ">=18" } }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/content-disposition": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", @@ -1740,102 +2755,297 @@ "url": "https://opencollective.com/express" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-dirname": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", + "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=0.4.0" } }, - "node_modules/convert-source-map": { + "node_modules/depd": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=6.6.0" - } + "optional": true }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "node_modules/dir-compare": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", + "dev": true, "license": "MIT", "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node_modules/dir-compare/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/dir-compare/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">= 8" + "node": "*" } }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/dmg-builder": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.15.3.tgz", + "integrity": "sha512-O3zJUFUYHJKgzPqioHxfxzBzlSC1eXCSr79gMSBKBP5AgjjpmrydMsMLotEg9fAJF36vdUncb+4ndRNxoPdlSQ==", + "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "fs-extra": "^10.1.0", + "js-yaml": "^4.1.0" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": ">=6.0" + "node": ">=12" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, "engines": { - "node": ">= 0.8" + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, "node_modules/dunder-proto": { @@ -1852,12 +3062,38 @@ "node": ">= 0.4" } }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron": { "version": "42.4.0", "resolved": "https://registry.npmjs.org/electron/-/electron-42.4.0.tgz", @@ -1877,6 +3113,63 @@ "node": ">= 22.12.0" } }, + "node_modules/electron-builder": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.15.3.tgz", + "integrity": "sha512-a1KM5heqS3gQCZzizXEI8RjJy3QVogULPdeSknt76uLDpBIW/HDGsMg/XgP0riP6PI9COsRvFITKKGDqA8fJxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "dmg-builder": "26.15.3", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.15.3.tgz", + "integrity": "sha512-Jc19XPV9y9+2bAdZPkXuVNGNIEFBq9poHC61l8Kv6FdK7DRG3+Ic0rerC0DXOaeHNz8yW0fg/JnF8GQROOF5MA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "26.15.3", + "builder-util": "26.15.3", + "electron-winstaller": "5.4.0" + } + }, + "node_modules/electron-publish": { + "version": "26.15.3", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.15.3.tgz", + "integrity": "sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "aws4": "^1.13.2", + "builder-util": "26.15.3", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "form-data": "^4.0.5", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.370", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.370.tgz", @@ -1914,6 +3207,66 @@ } } }, + "node_modules/electron-winstaller": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", + "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@electron/asar": "^3.2.1", + "debug": "^4.1.1", + "fs-extra": "^7.0.1", + "lodash": "^4.17.21", + "temp": "^0.9.0" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "@electron/windows-sign": "^1.1.2" + } + }, + "node_modules/electron-winstaller/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/electron-winstaller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-winstaller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/electron/node_modules/@types/node": { "version": "24.13.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz", @@ -1931,6 +3284,13 @@ "dev": true, "license": "MIT" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -1940,6 +3300,16 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -1950,6 +3320,13 @@ "node": ">=6" } }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -1980,6 +3357,30 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/esbuild": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", @@ -2038,6 +3439,20 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -2176,6 +3591,46 @@ } } }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", @@ -2197,6 +3652,46 @@ "url": "https://opencollective.com/express" } }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -2215,6 +3710,28 @@ "node": ">= 0.8" } }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2249,6 +3766,16 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2286,6 +3813,126 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -2298,6 +3945,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -2305,6 +3978,30 @@ "dev": true, "license": "ISC" }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -2317,6 +4014,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", @@ -2338,6 +4051,46 @@ "node": ">=16.9.0" } }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -2358,6 +4111,60 @@ "url": "https://opencollective.com/express" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -2382,18 +4189,76 @@ "node": ">= 0.10" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isbinaryfile": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz", + "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/jose": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", @@ -2409,6 +4274,29 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, + "node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -2422,6 +4310,13 @@ "node": ">=6" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -2434,6 +4329,14 @@ "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "license": "BSD-2-Clause" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "optional": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -2447,6 +4350,43 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -2459,6 +4399,16 @@ "loose-envify": "cli.js" } }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -2479,6 +4429,20 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -2497,6 +4461,24 @@ "node": ">= 0.8" } }, + "node_modules/mediabunny": { + "version": "1.50.8", + "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.50.8.tgz", + "integrity": "sha512-LgykLyQzhdpo0V2yw3UXmOpj+b4JAGdpHBwsPE6kjSt8Za0d1VllD+FV7EGHBcdV4+oHUAo+yrqbVAWxNSDCPQ==", + "license": "MPL-2.0", + "workspaces": [ + ".", + "packages/*" + ], + "dependencies": { + "@types/dom-mediacapture-transform": "^0.1.11", + "@types/dom-webcodecs": "0.1.13" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/Vanilagy" + } + }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", @@ -2509,6 +4491,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -2534,6 +4529,42 @@ "url": "https://opencollective.com/express" } }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", @@ -2557,6 +4588,20 @@ "node": ">= 18" } }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/mp4-muxer": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/mp4-muxer/-/mp4-muxer-5.2.2.tgz", @@ -2731,6 +4776,13 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-pty": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz", @@ -2767,6 +4819,19 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2788,6 +4853,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -2809,6 +4885,32 @@ "wrappy": "1" } }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -2818,6 +4920,16 @@ "node": ">= 0.8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2837,6 +4949,21 @@ "url": "https://opencollective.com/express" } }, + "node_modules/pe-library": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", + "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2866,6 +4993,52 @@ "node": ">=16.20.0" } }, + "node_modules/pkijs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", + "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@noble/hashes": "1.4.0", + "asn1js": "^3.0.6", + "bytestreamjs": "^2.0.1", + "pvtsutils": "^1.3.6", + "pvutils": "^1.1.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/pkijs/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, "node_modules/postcss": { "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", @@ -2895,6 +5068,36 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/proc-log": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", @@ -2905,6 +5108,13 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -2915,6 +5125,32 @@ "node": ">=0.4.0" } }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2928,6 +5164,37 @@ "node": ">= 0.10" } }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", + "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/qs": { "version": "6.15.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", @@ -2943,6 +5210,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -3008,36 +5288,144 @@ "react": "^18.3.1" } }, - "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-binary-file-arch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", + "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "bin": { + "read-binary-file-arch": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resedit": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", + "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/read-binary-file-arch": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", - "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", + "node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "MIT", + "license": "ISC", + "peer": true, "dependencies": { - "debug": "^4.3.4" + "glob": "^7.1.3" }, "bin": { - "read-binary-file-arch": "cli.js" + "rimraf": "bin.js" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0" } }, "node_modules/rollup": { @@ -3101,12 +5489,39 @@ "node": ">= 18" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/sanitize-filename": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", + "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -3126,6 +5541,14 @@ "semver": "bin/semver.js" } }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", @@ -3152,6 +5575,23 @@ "url": "https://opencollective.com/express" } }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/serve-static": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", @@ -3270,6 +5710,49 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -3280,6 +5763,35 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -3289,6 +5801,44 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -3302,6 +5852,19 @@ "node": ">= 8.0" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tar": { "version": "7.5.19", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.19.tgz", @@ -3329,6 +5892,52 @@ "node": ">=18" } }, + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } + }, + "node_modules/tiny-async-pool": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", + "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.5.0" + } + }, + "node_modules/tiny-async-pool/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -3346,6 +5955,26 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -3355,6 +5984,37 @@ "node": ">=0.6" } }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", @@ -3418,6 +6078,16 @@ "dev": true, "license": "MIT" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -3427,6 +6097,35 @@ "node": ">= 0.8" } }, + "node_modules/unzipper": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.12.5.tgz", + "integrity": "sha512-tXYOi9R57Uj/2Z25SOs5RRSzq886MBQj2gY8dPL+xl/kv6s6SvByoKfAtvfVeEuhntWDgjd2o9p2lb4TVPAz0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "~3.7.2", + "duplexer2": "~0.1.4", + "fs-extra": "11.3.1", + "graceful-fs": "^4.2.2", + "node-int64": "^0.4.0" + } + }, + "node_modules/unzipper/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -3467,6 +6166,20 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -4035,6 +6748,20 @@ "@esbuild/win32-x64": "0.28.1" } }, + "node_modules/webcrypto-core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.9.2.tgz", + "integrity": "sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.7.0", + "@peculiar/json-schema": "^1.1.12", + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4050,6 +6777,24 @@ "node": ">= 8" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4077,6 +6822,26 @@ } } }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -4084,6 +6849,48 @@ "dev": true, "license": "ISC" }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", diff --git a/package.json b/package.json index c7b9ef8..6c6b7f8 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,15 @@ "build": "electron-vite build", "start": "electron-vite preview", "typecheck": "tsc --noEmit -p tsconfig.web.json && tsc --noEmit -p tsconfig.node.json", + "pack": "bash scripts/pack.sh", "postinstall": "electron-rebuild -f -w node-pty" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.29.0", "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", - "mp4-muxer": "^5.1.5", + "mediabunny": "^1.50.8", + "mp4-muxer": "^5.2.2", "mp4box": "^2.3.0", "node-pty": "^1.1.0", "react": "^18.3.1", @@ -27,6 +29,7 @@ }, "devDependencies": { "@electron/rebuild": "^4.0.4", + "electron-builder": "^26.0.12", "@types/node": "^20.14.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", @@ -39,5 +42,10 @@ }, "contributors": [ "Claude (Anthropic)" - ] + ], + "allowScripts": { + "esbuild@0.25.12": true, + "esbuild@0.28.1": true, + "node-pty@1.1.0": true + } } diff --git a/scripts/e2e24.mjs b/scripts/e2e24.mjs index fc1495f..8e74e8e 100644 --- a/scripts/e2e24.mjs +++ b/scripts/e2e24.mjs @@ -1,5 +1,5 @@ // Test: Remotion fragments — create over a range (clip on a fresh top -// track, ≥60 fps meta), live preview overlay iframe, hot file edits keep it +// track, meta at the project fps), live preview overlay iframe, hot file edits keep it // alive, one-shot final render (vp9 alpha webm), export materialization // composites the fragment over a video, MCP exposes kadr_fragment_create. // Requires the workspace to be installed (first fragmentEnsure run). @@ -114,11 +114,13 @@ try { const overlapping = track?.clips.filter(c => c.id !== r.clipId && c.start < 2 && c.start + c.duration > 1).length return { id: r.id, entry: r.entry, kind: clip?.kind, fps: clip?.fragmentMeta?.fps, - trackKind: track?.kind, overlapping } + projectFps: st().project.fps, trackKind: track?.kind, overlapping } })()`) fragId = created.id + // the fragment samples at the project fps: forcing ≥60 doubled the PNG frame + // pile for 30 fps projects with no visible gain in a 30 fps export check('fragment created as a remotion clip on a free video track', - created.kind === 'remotion' && created.fps >= 60 && + created.kind === 'remotion' && created.fps === created.projectFps && created.trackKind === 'video' && created.overlapping === 0, JSON.stringify(created)) @@ -189,8 +191,8 @@ try { partsOf().length === 0 && existsSync(rendered.path), partsOf().join(',')) const probe = execFileSync('ffprobe', ['-v', 'error', '-show_entries', 'stream=codec_name,width,r_frame_rate', '-of', 'csv', rendered.path]).toString() - check('render is vp9 at 60 fps, project width', - probe.includes('vp9') && probe.includes('60/1'), probe.trim().split('\n')[0]) + check('render is vp9 at the project fps, project width', + probe.includes('vp9') && probe.includes(`${created.fps}/1`), probe.trim().split('\n')[0]) // 5) export composites the fragment over an underlying video await evalJs(`(async () => { diff --git a/scripts/install-desktop.sh b/scripts/install-desktop.sh new file mode 100755 index 0000000..2a1d50a --- /dev/null +++ b/scripts/install-desktop.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# Register (or remove) a Kadr menu entry so the app launches from the +# application launcher / by double-click — no terminal. Ships as install.sh at +# the root of the tar.gz archive next to the `kadr` executable. Run once after +# putting the folder in its final location (e.g. /opt/kadr): +# +# bash ./install.sh # add to the app menu +# bash ./install.sh --uninstall # remove it +# +# Run as your normal user (NOT sudo) even if the app lives in /opt — it writes +# only under ~/.local/share, and stores the ABSOLUTE path of the app in the +# .desktop, so re-run it if you move the folder. +set -euo pipefail + +DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)" +APPS="$HOME/.local/share/applications" +ICONS="$HOME/.local/share/icons/hicolor/512x512/apps" +DESKTOP="$APPS/kadr.desktop" +ICON_DST="$ICONS/kadr.png" + +refresh() { + command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$APPS" || true + command -v gtk-update-icon-cache >/dev/null 2>&1 && gtk-update-icon-cache -q -t "$HOME/.local/share/icons/hicolor" || true + command -v kbuildsycoca6 >/dev/null 2>&1 && kbuildsycoca6 --noincremental >/dev/null 2>&1 || \ + command -v kbuildsycoca5 >/dev/null 2>&1 && kbuildsycoca5 --noincremental >/dev/null 2>&1 || true +} + +if [ "${1:-}" = "--uninstall" ]; then + rm -f "$DESKTOP" "$ICON_DST" + refresh + echo "Removed Kadr menu entry." + exit 0 +fi + +[ -x "$DIR/kadr" ] || { echo "error: 'kadr' executable not found next to this script ($DIR)" >&2; exit 1; } + +mkdir -p "$APPS" "$ICONS" + +# icon: prefer the one shipped at the archive root; fall back to any bundled png +if [ -f "$DIR/kadr.png" ]; then + cp -f "$DIR/kadr.png" "$ICON_DST" +else + found="$(find "$DIR/resources" -name '*.png' 2>/dev/null | head -n1 || true)" + [ -n "$found" ] && cp -f "$found" "$ICON_DST" || true +fi + +cat > "$DESKTOP" </dev/null || true + +refresh +echo "Installed. Look for “Kadr” in your application launcher." +echo "(Moved the folder? Re-run this script to fix the path.)" diff --git a/scripts/pack.sh b/scripts/pack.sh new file mode 100755 index 0000000..649fd4d --- /dev/null +++ b/scripts/pack.sh @@ -0,0 +1,189 @@ +#!/usr/bin/env bash +# Build the self-contained Kadr archive (tar.gz + AppImage) for linux-x64. +# +# Run INSIDE a build toolchain (e.g. a Fedora distrobox), NOT on a bare +# immutable host — node-pty is compiled here once against the bundled Electron: +# +# distrobox create --name kadr-build --image fedora:42 +# distrobox enter kadr-build +# sudo dnf install -y gcc-c++ make python3 nodejs npm tar xz which +# cd ~/Projects/kadr && bash scripts/pack.sh +# +# Output: dist/kadr--linux-x64.tar.gz and dist/Kadr--linux-x86_64.AppImage +# +# Flags: +# --lite core editor + ffmpeg only (skip whisper + Remotion seed) +# --model pre-bundle a faster-whisper model; repeat for several +# (e.g. --model base --model large-v3) — the first is default +# --skip-build reuse an existing out/ (don't re-run npm ci / electron-vite) +set -euo pipefail + +# ---- pinned versions (bump as needed) -------------------------------------- +NODE_VERSION="20.18.1" # bundled host node for npm/npx/mcp bridge +PBS_RELEASE="20241206" # astral python-build-standalone release tag +PYTHON_VERSION="3.12.8" # cpython version within that release +# BtbN static build (GPL): unlike johnvansickle it ships h264_nvenc/hevc_nvenc, +# so the "GPU encoding (NVENC)" export option works in the packaged app too. It +# dlopens the user's NVIDIA driver libs at runtime — no NVIDIA, no problem, the +# app's nvencAvailable() check just leaves the option off and x264 is used. +# +# PINNED, not `latest`, on purpose: BtbN's current builds require NVENC API 13.1 +# (driver ≥ 610), so h264_nvenc won't open on the common 5xx driver line. The +# 2026-05-31 dated build is the last one built against nv-codec-headers API 13.0 +# — verified encoding on driver 580 — and works on 610+ too via NVENC back-compat. +# Bump to `latest` once driver ≥ 610 is the norm. (BtbN prunes old dated builds +# eventually; if this 404s, pick the newest dated build whose h264_nvenc still +# opens on the target driver — see nvencAvailable()'s 1-frame test-encode.) +FFMPEG_URL="https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-05-31-13-22/ffmpeg-N-124714-g49a77d37be-linux64-gpl.tar.xz" + +ARCH="x64"; UNAME_ARCH="x86_64" +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +RUNTIME="$ROOT/runtime" +SEED="$ROOT/kadr-fragments-seed" +BIN="$RUNTIME/bin" + +LITE=0; WHISPER_MODELS=""; SKIP_BUILD=0 +while [ $# -gt 0 ]; do + case "$1" in + --lite) LITE=1 ;; + --model) WHISPER_MODELS="$WHISPER_MODELS ${2:-}"; shift ;; + --skip-build) SKIP_BUILD=1 ;; + *) echo "unknown flag: $1" >&2; exit 2 ;; + esac + shift +done + +log() { printf '\n\033[1;36m==>\033[0m %s\n' "$*"; } +need() { command -v "$1" >/dev/null 2>&1 || { echo "missing tool: $1" >&2; exit 1; }; } +dl() { echo " fetch $1"; curl -fsSL --retry 3 -o "$2" "$1"; } + +need curl; need tar; need node; need npm +cd "$ROOT" + +# ---- 0. clean staging ------------------------------------------------------ +log "clean staging (runtime/, kadr-fragments-seed/)" +rm -rf "$RUNTIME" "$SEED" +mkdir -p "$BIN" + +# ---- 1. app build ---------------------------------------------------------- +if [ "$SKIP_BUILD" -eq 0 ]; then + log "npm ci + electron-vite build" + npm ci + npm run build +else + log "reusing existing out/ (--skip-build)" + [ -d "$ROOT/out/main" ] || { echo "out/ missing — drop --skip-build" >&2; exit 1; } +fi + +# ---- 2. ffmpeg / ffprobe (static, with NVENC) ------------------------------ +log "stage static ffmpeg/ffprobe (BtbN gpl, incl. nvenc)" +TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT +dl "$FFMPEG_URL" "$TMP/ffmpeg.tar.xz" +tar -xJf "$TMP/ffmpeg.tar.xz" -C "$TMP" +FF_DIR="$(find "$TMP" -maxdepth 1 -type d -name 'ffmpeg-*' | head -n1)" +# BtbN layout is /bin/ffmpeg; johnvansickle was /ffmpeg +install -m755 "$FF_DIR/bin/ffmpeg" "$BIN/ffmpeg" +install -m755 "$FF_DIR/bin/ffprobe" "$BIN/ffprobe" +if "$BIN/ffmpeg" -hide_banner -encoders 2>/dev/null | grep -q h264_nvenc; then + log " ffmpeg has NVENC ✓" +else + log " WARNING: bundled ffmpeg lacks nvenc" +fi + +# ---- 3. bundled Node (host node/npm/npx for Remotion + MCP bridge) --------- +log "stage Node $NODE_VERSION" +NODE_TARBALL="node-v$NODE_VERSION-linux-$ARCH.tar.xz" +dl "https://nodejs.org/dist/v$NODE_VERSION/$NODE_TARBALL" "$TMP/$NODE_TARBALL" +tar -xJf "$TMP/$NODE_TARBALL" -C "$RUNTIME" +mv "$RUNTIME/node-v$NODE_VERSION-linux-$ARCH" "$RUNTIME/node" +ln -sf ../node/bin/node "$BIN/node" +ln -sf ../node/bin/npm "$BIN/npm" +ln -sf ../node/bin/npx "$BIN/npx" + +# ---- 4. Python + faster-whisper (relocatable) ------------------------------ +if [ "$LITE" -eq 0 ]; then + log "stage relocatable Python $PYTHON_VERSION + faster-whisper" + PBS_FILE="cpython-$PYTHON_VERSION+$PBS_RELEASE-$UNAME_ARCH-unknown-linux-gnu-install_only.tar.gz" + PBS_URL="https://github.com/astral-sh/python-build-standalone/releases/download/$PBS_RELEASE/$PBS_FILE" + dl "$PBS_URL" "$TMP/python.tar.gz" + tar -xzf "$TMP/python.tar.gz" -C "$RUNTIME" # extracts to runtime/python/ + "$RUNTIME/python/bin/python3" -m pip install --no-cache-dir --upgrade pip + "$RUNTIME/python/bin/python3" -m pip install --no-cache-dir faster-whisper + ln -sf ../python/bin/python3 "$BIN/python3" + + # Pre-bundle one or more faster-whisper models. Each is cached into the shared + # HF tree runtime/whisper-seed (electron-builder ships it); at runtime + # transcribe.ts copies ALL of them into the writable ~/.cache/huggingface so + # none is re-downloaded. whisper-model.txt records the FIRST as the app default; + # standard sizes (base/medium/large-v3) are also selectable in the UI. + if [ -n "$WHISPER_MODELS" ]; then + DEFAULT_MODEL="" + for M in $WHISPER_MODELS; do + log "pre-download whisper model: $M" + HF_HOME="$RUNTIME/whisper-seed" \ + "$RUNTIME/python/bin/python3" - "$M" <<'PY' +import sys +from faster_whisper import WhisperModel +WhisperModel(sys.argv[1], device="cpu", compute_type="int8") +print("model cached:", sys.argv[1]) +PY + if [ -z "$DEFAULT_MODEL" ]; then DEFAULT_MODEL="$M"; fi + done + if [ ! -d "$RUNTIME/whisper-seed/hub" ]; then + echo "ERROR: models '$WHISPER_MODELS' did not land in runtime/whisper-seed" >&2; exit 1 + fi + printf '%s' "$DEFAULT_MODEL" > "$RUNTIME/whisper-model.txt" + echo " bundled models [$WHISPER_MODELS ] → whisper-seed ($(du -sh "$RUNTIME/whisper-seed" | cut -f1)); default=$DEFAULT_MODEL" + fi +else + log "lite build — skipping Python/whisper" +fi + +# ---- 5. Remotion fragments seed (pre-installed node_modules) --------------- +if [ "$LITE" -eq 0 ]; then + log "build Remotion seed (node_modules via bundled npm)" + SEED_TMP="$(mktemp -d)" + # deps mirror electron/fragments.ts PKG_JSON — keep in sync if that changes. + cat > "$SEED_TMP/package.json" <<'JSON' +{ + "name": "kadr-fragments", + "private": true, + "dependencies": { + "@remotion/cli": "4.0.247", + "@remotion/player": "4.0.247", + "react": "18.3.1", + "react-dom": "18.3.1", + "remotion": "4.0.247" + }, + "devDependencies": { + "@types/react": "18.3.3", + "@vitejs/plugin-react": "4.3.1", + "typescript": "5.5.4", + "vite": "5.4.8" + } +} +JSON + ( cd "$SEED_TMP" && PATH="$BIN:$PATH" npm install --no-audit --no-fund ) + # Warm Remotion's headless chrome so a first render is offline when the cache + # lands inside node_modules; harmless (best-effort) otherwise — a first + # render then fetches chrome over the network using the bundled node. + ( cd "$SEED_TMP" && PATH="$BIN:$PATH" npx remotion browser ensure ) || \ + echo " (browser warm skipped — first render will fetch chrome over network)" + mkdir -p "$SEED" + mv "$SEED_TMP/node_modules" "$SEED/node_modules" + rm -rf "$SEED_TMP" +else + log "lite build — skipping Remotion seed" +fi + +# ---- 6. package ------------------------------------------------------------ +# refresh the launcher icon from SVG if a rasterizer is present; otherwise the +# committed build/icon.png is used as-is. +if command -v magick >/dev/null 2>&1; then magick -background none "$ROOT/build/icon.svg" -resize 512x512 "$ROOT/build/icon.png" +elif command -v convert >/dev/null 2>&1; then convert -background none "$ROOT/build/icon.svg" -resize 512x512 "$ROOT/build/icon.png"; fi + +log "electron-builder → tar.gz + AppImage" +npx electron-builder --linux tar.gz AppImage + +log "done. artifacts in dist/" +ls -lh "$ROOT/dist" 2>/dev/null || true diff --git a/shared/rawEncode.ts b/shared/rawEncode.ts index 0e4833b..6978607 100644 --- a/shared/rawEncode.ts +++ b/shared/rawEncode.ts @@ -32,7 +32,13 @@ export function rawEncodeArgs(o: RawEncodeOpts): string[] { '-s', `${o.width}x${o.height}`, '-r', String(o.fps), '-i', 'pipe:0', '-vf', vf, - ...(o.codec === 'libx264' + ...(o.codec === 'h264_nvenc' + // NVIDIA hardware encoder — moves H.264 off the CPU (x264) onto the GPU's + // dedicated NVENC block; p5/vbr is a balanced quality preset. + ? ['-c:v', 'h264_nvenc', '-preset', 'p5', '-tune', 'hq', '-rc', 'vbr', + '-b:v', String(b), '-maxrate', String(Math.round(b * 1.6)), '-bufsize', String(b * 3), + '-profile:v', 'high'] + : o.codec === 'libx264' ? ['-c:v', 'libx264', '-preset', 'faster', '-b:v', String(b), '-maxrate', String(Math.round(b * 1.6)), '-bufsize', String(b * 3)] : ['-c:v', o.codec, '-b:v', String(b), diff --git a/shared/types.ts b/shared/types.ts index 118acf4..f32d663 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -21,6 +21,8 @@ export interface MediaAsset { height: number fps: number hasAudio: boolean + /** ffprobe audio codec_name (e.g. 'aac', 'ac3'); drives preview-proxy need */ + audioCodec?: string /** data: URL of a poster frame, generated on import */ thumbnail?: string /** poster of the last frame (clip tails show it on the timeline) */ @@ -36,6 +38,9 @@ export interface MediaAsset { proxyPath?: string /** this asset is a reversed render of a source range of another asset */ reverseOf?: { assetId: string; start: number; duration: number } + /** source file mtime at probe time — lets a re-import of an overwritten + same-name file refresh the asset instead of reusing the stale one */ + mtimeMs?: number } export type Easing = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'hold' @@ -86,7 +91,7 @@ export interface ClipMask { bottom: Anim } -export type MaskShapeType = 'rect' | 'ellipse' | 'triangle' +export type MaskShapeType = 'rect' | 'ellipse' | 'triangle' | 'roundrect' /** Drawn shape mask in layer UV space (0..1), with soft borders. */ export interface MaskShape { @@ -98,6 +103,8 @@ export interface MaskShape { /** soft border inward / outward, in layer-height fractions */ featherIn: Anim featherOut: Anim + /** corner radius for 'roundrect', in layer-height fractions (0 = sharp) */ + radius?: Anim /** exclude mode: the shape cuts a hole instead of keeping its inside */ invert: boolean } @@ -223,6 +230,8 @@ export interface PoseShape { h: number featherIn: number featherOut: number + /** corner radius for 'roundrect' (y-height fractions) */ + radius?: number } export interface PosePreset { @@ -874,6 +883,8 @@ export interface StoragePruneResult { export interface KadrApi { openMediaDialog(): Promise probeMedia(path: string): Promise + /** cheap stat for the import dedupe (mtime/size), or null if unreadable */ + statMedia(path: string): Promise<{ mtimeMs: number; size: number } | null> fileUrl(path: string): string /** Absolute path of a File dropped from the OS (File.path is gone since Electron 32 — this goes through webUtils.getPathForFile). */ @@ -917,9 +928,11 @@ export interface KadrApi { ): () => void /** Build (or reuse) a preview proxy; resolves with the proxy file path. - Alpha sources get a VP9+alpha WebM proxy instead of H.264. */ + Alpha sources get a VP9+alpha WebM proxy instead of H.264; audioOnly + transcodes just the audio to AAC (a music/voice file whose codec Chromium + can't decode). */ requestProxy(path: string, duration: number, - opts?: { alpha?: boolean; codec?: string }): Promise + opts?: { alpha?: boolean; codec?: string; audioOnly?: boolean }): Promise onProxyProgress(cb: (p: { path: string; progress: number }) => void): () => void /** Full-resolution intermediate for sources Chromium cannot decode (e.g. HEVC without VAAPI, ProRes): H.264, or VP9+alpha WebM for alpha @@ -988,6 +1001,8 @@ export interface KadrApi { onFragmentProgress(cb: (p: { id: string; phase: string; progress: number }) => void): () => void /** Mix the request's audio to a temp wav and run Whisper over it. */ + /** whisper model pre-bundled in the packaged app ('' if none); subtitles default to it */ + defaultWhisperModel: string transcribe(req: TranscribeRequest): Promise transcribeCancel(): Promise onTranscribeProgress(cb: (p: { progress: number; text: string }) => void): () => void @@ -1043,4 +1058,32 @@ export interface KadrApi { claudeClose(): Promise onClaudeData(cb: (data: string) => void): () => void onClaudeExit(cb: (code: number) => void): () => void + + /** WebGL powerPreference hint derived from the persisted GPU choice + ('default' | 'high-performance' | 'low-power'); '' when unset. */ + defaultGpuPower: string + /** True when NVIDIA hardware H.264 encoding (NVENC) is usable — an NVIDIA + device is present and ffmpeg has h264_nvenc. */ + nvencAvailable(): Promise + /** Enumerate selectable GPUs (DRM render nodes). */ + gpuList(): Promise + /** Confirm the current GPU trial works (only reachable if the window renders); + makes the choice stick instead of reverting to auto next launch. */ + gpuConfirm(): void + /** Relaunch the app so a new GPU choice takes effect. */ + relaunchApp(): void +} + +/** A user-selectable GPU (one DRM render node). */ +export interface GpuInfo { + /** DRM render node path, e.g. /dev/dri/renderD129 */ + node: string + /** kernel driver: i915 / nvidia / amdgpu / … */ + driver: string + vendorId: string + deviceId: string + /** friendly vendor: NVIDIA / Intel / AMD (or driver name) */ + vendor: string + /** primary display adapter (laptop iGPU) vs a discrete card */ + integrated: boolean } diff --git a/src/App.tsx b/src/App.tsx index cecd7a8..00363a5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,9 +6,11 @@ import { Inspector } from './components/Inspector' import { Timeline } from './components/Timeline' import { TransportBar, LangSwitch } from './components/TransportBar' import { ExportDialog } from './components/ExportDialog' +import { SettingsDialog } from './components/SettingsDialog' import { ClaudePanel } from './components/ClaudePanel' import { TranscribeDialog, SubtitlePanel } from './components/TextTools' import { CaptionsDialog } from './components/CaptionsDialog' +import { ProjectFormatDialog, type Dims } from './components/ProjectFormatDialog' import { NeonWaveDialog } from './components/NeonWaveDialog' import { TtsSettingsDialog, SpeakDialog, refreshTtsKey } from './components/TtsDialog' import { DefectsDialog } from './components/DefectsDialog' @@ -19,7 +21,13 @@ import { modalsOpen } from './components/Modal' import { rememberProject } from './engine/storage' import { usePopout, previewHost, setPreviewSlot, dockPreviewWindow } from './engine/popout' import { useEditor, newProject } from './state/store' -import { dropPayload, dropUsable, importDrop, importFiles } from './engine/mediaImport' +import { + dropPayload, + dropUsable, + importDrop, + importFiles, + setProjectFormatResolver +} from './engine/mediaImport' import { syncProjectFragments } from './engine/fragments' import { useT, type TKey } from './i18n' import { create } from 'zustand' @@ -121,6 +129,16 @@ export default function App() { const [sideW, setSideW] = useState(() => Math.min(640, Math.max(200, Number(localStorage.getItem('kadr.sidew')) || 280)) ) + // New Project format dialog + const [newProjOpen, setNewProjOpen] = useState(false) + // "media dropped into an empty project" format prompt: the pending probe + + // the resolver waiting for the user's pick + const [dropFmt, setDropFmt] = useState<{ probe: Dims; resolve: (d: Dims | null) => void } | null>(null) + + useEffect(() => { + setProjectFormatResolver((probe) => new Promise((resolve) => setDropFmt({ probe, resolve }))) + return () => setProjectFormatResolver(null) + }, []) // media dropped ANYWHERE in the window is at least imported into the bin // (the timeline zones place clips and mark the event handled); a non-media @@ -293,13 +311,7 @@ export default function App() { - +
@@ -364,6 +385,35 @@ export default function App() {
+ + {newProjOpen && ( + { + useEditor.getState().setProject(newProject(d)) + markProjectSaved(useEditor.getState().project) + setNewProjOpen(false) + }} + onClose={() => setNewProjOpen(false)} + /> + )} + {dropFmt && ( + { + dropFmt.resolve(d) + setDropFmt(null) + }} + onClose={() => { + dropFmt.resolve(null) + setDropFmt(null) + }} + /> + )} diff --git a/src/components/AnimEditor.tsx b/src/components/AnimEditor.tsx index fd52f45..5b77370 100644 --- a/src/components/AnimEditor.tsx +++ b/src/components/AnimEditor.tsx @@ -50,14 +50,15 @@ const MASK_PARAMS: ParamDef[] = [ { key: 'mB', label: 'maskBottom', step: 0.01, min: 0, max: 0.49, get: (c) => maskOf(c).bottom, patch: (c, a) => ({ mask: { ...maskOf(c), bottom: a } }) } ] -type ShapeField = 'cx' | 'cy' | 'w' | 'h' | 'featherIn' | 'featherOut' +type ShapeField = 'cx' | 'cy' | 'w' | 'h' | 'featherIn' | 'featherOut' | 'radius' function shapeParam( i: number, field: ShapeField, label: TKey, step: number, min: number, max: number ): ParamDef { + const zeroDefault = field === 'featherIn' || field === 'featherOut' || field === 'radius' return { key: `s${i}.${field}`, label, step, min, max, - get: (c) => shapesOf(c)[i]?.[field] ?? defAnim(field === 'featherIn' || field === 'featherOut' ? 0 : 0.5), + get: (c) => shapesOf(c)[i]?.[field] ?? defAnim(zeroDefault ? 0 : 0.5), patch: (c, a) => patchShapes(shapesOf(c).map((s, j) => (j === i ? { ...s, [field]: a } : s))) } } @@ -423,7 +424,8 @@ export function AnimEditor({ width }: { width: number }) { const newShape: MaskShape = { type, cx: defAnim(a.u), cy: defAnim(a.v), w: defAnim(0.05), h: defAnim(0.05), - featherIn: defAnim(0), featherOut: defAnim(0), invert: false + featherIn: defAnim(0), featherOut: defAnim(0), invert: false, + ...(type === 'roundrect' ? { radius: defAnim(0.08) } : {}) } const f0 = findClip(st.project, clip.id) if (!f0) return @@ -506,7 +508,8 @@ export function AnimEditor({ width }: { width: number }) { invert: s.invert, cx: evalAnim(s.cx, rel), cy: evalAnim(s.cy, rel), w: evalAnim(s.w, rel), h: evalAnim(s.h, rel), - featherIn: evalAnim(s.featherIn, rel), featherOut: evalAnim(s.featherOut, rel) + featherIn: evalAnim(s.featherIn, rel), featherOut: evalAnim(s.featherOut, rel), + ...(s.radius ? { radius: evalAnim(s.radius, rel) } : {}) })) }) } @@ -555,7 +558,10 @@ export function AnimEditor({ width }: { width: number }) { }) const f2 = findClip(useEditor.getState().project, clip.id) if (f2) useEditor.getState().updateClip(clip.id, patchShapes( - shapesOf(f2.clip).map((s, i) => ({ ...s, type: ps[i].type, invert: ps[i].invert })) + shapesOf(f2.clip).map((s, i) => ({ + ...s, type: ps[i].type, invert: ps[i].invert, + ...(ps[i].radius != null ? { radius: defAnim(ps[i].radius!) } : {}) + })) )) } else { // different layout — replace with the preset's shapes as static values @@ -563,7 +569,8 @@ export function AnimEditor({ width }: { width: number }) { type: s.type, invert: s.invert, cx: defAnim(s.cx), cy: defAnim(s.cy), w: defAnim(s.w), h: defAnim(s.h), - featherIn: defAnim(s.featherIn), featherOut: defAnim(s.featherOut) + featherIn: defAnim(s.featherIn), featherOut: defAnim(s.featherOut), + ...(s.radius != null ? { radius: defAnim(s.radius) } : {}) })))) } } @@ -778,6 +785,7 @@ export function AnimEditor({ width }: { width: number }) { const shapeTools: { id: Tool; icon: IconName; title: TKey }[] = [ { id: 'edges', icon: 'crop', title: 'toolEdges' }, { id: 'rect', icon: 'square', title: 'toolRect' }, + { id: 'roundrect', icon: 'squircle', title: 'toolRoundrect' }, { id: 'ellipse', icon: 'circle', title: 'toolEllipse' }, { id: 'triangle', icon: 'triangle', title: 'toolTriangle' } ] @@ -925,6 +933,19 @@ export function AnimEditor({ width }: { width: number }) { onChange={(e) => write(selShapeParams[5], Number(e.target.value))} /> + {sel.type === 'roundrect' && ( + + )}
)} diff --git a/src/components/CaptionsDialog.tsx b/src/components/CaptionsDialog.tsx index 49293c6..2fc0d45 100644 --- a/src/components/CaptionsDialog.tsx +++ b/src/components/CaptionsDialog.tsx @@ -17,7 +17,8 @@ export function CaptionsDialog() { const range = useEditor((s) => s.range) const [style, setStyle] = useState({ ...CAPTION_DEFAULTS }) const [maxWords, setMaxWords] = useState(3) - const [model, setModel] = useState('large-v3') + const bundledModel = window.kadr.defaultWhisperModel + const [model, setModel] = useState(bundledModel || 'large-v3') const [language, setLanguage] = useState('auto') const [running, setRunning] = useState(false) const [progress, setProgress] = useState(0) @@ -91,6 +92,9 @@ export function CaptionsDialog() { + {status.kind === 'running' && (
diff --git a/src/components/ProjectFormatDialog.tsx b/src/components/ProjectFormatDialog.tsx new file mode 100644 index 0000000..a8cf8f5 --- /dev/null +++ b/src/components/ProjectFormatDialog.tsx @@ -0,0 +1,79 @@ +import { useState } from 'react' +import { PROJECT_PRESETS } from '@/presets' +import { useT } from '@/i18n' +import { Modal } from './Modal' +import { Icon } from './icons' + +export interface Dims { + width: number + height: number + fps: number +} + +/** + * Pick a project canvas format. Mounted only while open (so the selection + * resets each time). Two uses: the New Project dialog (matchVideo = null) and + * the "media dropped into an empty project" prompt (matchVideo = the clip's + * dimensions, offered as the preselected "match video" option). + */ +export function ProjectFormatDialog({ + matchVideo, + title, + applyLabel, + onApply, + onClose +}: { + matchVideo: Dims | null + title: string + applyLabel: string + onApply: (dims: Dims) => void + onClose: () => void +}) { + const t = useT() + const [sel, setSel] = useState(matchVideo ? 'match' : PROJECT_PRESETS[0].id) + + const dims = (): Dims => { + if (sel === 'match' && matchVideo) { + return { + width: matchVideo.width, + height: matchVideo.height, + fps: Math.round(matchVideo.fps) || 30 + } + } + const p = PROJECT_PRESETS.find((x) => x.id === sel) ?? PROJECT_PRESETS[0] + return { width: p.width, height: p.height, fps: p.fps } + } + + return ( + } + wide + actions={ + <> + + + + } + > + + + ) +} diff --git a/src/components/SettingsDialog.tsx b/src/components/SettingsDialog.tsx new file mode 100644 index 0000000..d456b80 --- /dev/null +++ b/src/components/SettingsDialog.tsx @@ -0,0 +1,120 @@ +import { useEffect, useState } from 'react' +import type { GpuInfo } from '@shared/types' +import { useEditor } from '@/state/store' +import { useT } from '@/i18n' +import { Modal } from './Modal' +import { Icon } from './icons' + +/** Read the currently-active WebGL renderer string (e.g. "Mesa Intel(R) UHD + * Graphics 630 …" or an NVIDIA string) so the user can confirm a GPU switch + * actually took effect after relaunch. */ +function activeRenderer(): string { + try { + const gl = document.createElement('canvas').getContext('webgl2') + if (!gl) return '' + const ext = gl.getExtension('WEBGL_debug_renderer_info') + return ext ? String(gl.getParameter(ext.UNMASKED_RENDERER_WEBGL)) : '' + } catch { + return '' + } +} + +export function SettingsDialog() { + const t = useT() + const open = useEditor((s) => s.settingsOpen) + const close = () => useEditor.getState().setSettingsOpen(false) + + const [gpus, setGpus] = useState([]) + const [node, setNode] = useState('') // dropdown selection ('' = auto) + const [savedNode, setSavedNode] = useState('') + const [savedStatus, setSavedStatus] = useState(undefined) + const [renderer] = useState(activeRenderer) + + useEffect(() => { + if (!open) return + let alive = true + void (async () => { + const [list, choice] = await Promise.all([ + window.kadr.gpuList(), + window.kadr.readUserStore('gpu') as Promise<{ node?: string; status?: string } | null> + ]) + if (!alive) return + setGpus(list) + setSavedNode(choice?.node ?? '') + setSavedStatus(choice?.status) + const chosen = list.find((g) => g.node === choice?.node) + const matches = !!chosen && renderer.toUpperCase().includes(chosen.vendor.toUpperCase()) + setNode(choice?.node && (choice.status === 'ok' || matches) ? choice.node : '') + })() + return () => { + alive = false + } + }, [open]) + + if (!open) return null + + const label = (g: GpuInfo) => + `${g.vendor} (${g.integrated ? t('gpuIntegrated') : t('gpuDiscrete')})` + + const chosen = gpus.find((g) => g.node === savedNode) + const rendererMatches = !!chosen && renderer.toUpperCase().includes(chosen.vendor.toUpperCase()) + // The trial GPU is actually driving the window (we can see it, and its vendor + // shows in the live renderer) but hasn't been kept yet — offer to keep it. + const trialPending = savedStatus !== 'ok' && !!savedNode && rendererMatches + // A chosen GPU that didn't come up: it's on disk but the app fell back to auto. + const reverted = savedStatus !== 'ok' && !!savedNode && !rendererMatches + const effectiveSaved = savedNode && (savedStatus === 'ok' || trialPending) ? savedNode : '' + const changed = node !== effectiveSaved + + const applyRestart = async () => { + // written as a 'trial': main flips it to 'failed' before the window opens + // and it only becomes 'ok' when the user keeps it below — so a GPU that + // comes up blank (or not at all) heals back to auto on the next launch. + await window.kadr.writeUserStore('gpu', node ? { node, status: 'trial' } : {}) + window.kadr.relaunchApp() + } + + const keep = () => { + window.kadr.gpuConfirm() + setSavedStatus('ok') + } + + return ( + } + wide + actions={ + <> + + {trialPending && !changed && ( + + )} + {changed && ( + + )} + + } + > + + + {renderer &&
{t('gpuActive')}: {renderer}
} + {reverted &&
{t('gpuFailedRevert')}
} +
{t('gpuEncodeNote')}
+ + {trialPending && !changed &&
{t('gpuTrialAsk')}
} + {changed &&
{t('gpuRestartHint')}
} +
+ ) +} diff --git a/src/components/TextTools.tsx b/src/components/TextTools.tsx index c3d311f..17fdaa3 100644 --- a/src/components/TextTools.tsx +++ b/src/components/TextTools.tsx @@ -32,7 +32,8 @@ export const useTextUi = create((set) => ({ export function TranscribeDialog() { const t = useT() const target = useTextUi((s) => s.transcribeTarget) - const [model, setModel] = useState('large-v3') + const bundledModel = window.kadr.defaultWhisperModel + const [model, setModel] = useState(bundledModel || 'large-v3') const [language, setLanguage] = useState('auto') const [timecodes, setTimecodes] = useState<'absolute' | 'relative'>('absolute') const [maxWords, setMaxWords] = useState(3) @@ -111,6 +112,9 @@ export function TranscribeDialog() {