From ff24f5a1a629645beeb1b0b47b3305e00d73398f Mon Sep 17 00:00:00 2001 From: Israel Fernandes Date: Sun, 1 Nov 2026 14:17:06 -0300 Subject: [PATCH 1/4] Desktop shell: Electron app with graphics backend, translation, Creality projects and agent pairing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit desktop/ wraps the local build in Electron (Windows x64): - Help → Hardware picks the ANGLE backend (DirectX 11 default, DirectX 12, Vulkan, OpenGL) and shows GPU, WebGPU, threads, isolation and WebNN. - The page is translated at run time from i18n/.json, node by node, and kept translated as React redraws; pt-BR today, first language from the OS, Language menu to switch. The app source is not touched. - Creality Scan project.obp opens from menu, CLI, drop or file picker, resolving to result/Mesh.ply; a folder watcher offers each new mesh. - The app runs the relay itself and writes agent.json; scanruler-mcp.cmd next to the exe joins that app so agents drive the open window. - Tests: smoke (isolation, threads, WebGL2, WebGPU), Creality watcher, i18n overlay, agent relay end to end; each on its own user-data dir. Co-Authored-By: Claude Fable 5.1 --- README.md | 7 + desktop/.gitignore | 2 + desktop/README.md | 45 + desktop/agent-bridge.mjs | 15 + desktop/creality.js | 55 + desktop/hardware.html | 48 + desktop/i18n.js | 109 + desktop/i18n/pt-BR.json | 347 ++++ desktop/main.js | 326 +++ desktop/package-lock.json | 3899 ++++++++++++++++++++++++++++++++++++ desktop/package.json | 81 + desktop/preload-main.cjs | 8 + desktop/preload.cjs | 10 + desktop/scanruler-mcp.cmd | 5 + desktop/settings.js | 51 + desktop/test/creality.mjs | 51 + desktop/test/e2e-agent.mjs | 41 + desktop/test/i18n.mjs | 46 + desktop/test/smoke.mjs | 54 + 19 files changed, 5200 insertions(+) create mode 100644 desktop/.gitignore create mode 100644 desktop/README.md create mode 100644 desktop/agent-bridge.mjs create mode 100644 desktop/creality.js create mode 100644 desktop/hardware.html create mode 100644 desktop/i18n.js create mode 100644 desktop/i18n/pt-BR.json create mode 100644 desktop/main.js create mode 100644 desktop/package-lock.json create mode 100644 desktop/package.json create mode 100644 desktop/preload-main.cjs create mode 100644 desktop/preload.cjs create mode 100644 desktop/scanruler-mcp.cmd create mode 100644 desktop/settings.js create mode 100644 desktop/test/creality.mjs create mode 100644 desktop/test/e2e-agent.mjs create mode 100644 desktop/test/i18n.mjs create mode 100644 desktop/test/smoke.mjs diff --git a/README.md b/README.md index c967c88e..4b0bcb21 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,13 @@ is processed by the agent and goes to its model's provider, unless the model runs on your computer. Setup per agent, troubleshooting and every option are in [mcp/README.md](mcp/README.md). +## Desktop shell + +An Electron shell for Windows lives in [desktop/](desktop/README.md): it runs the +local build with no browser, picks the graphics backend (DirectX 11/12, Vulkan, +OpenGL), translates the page at run time (Portuguese today), opens Creality Scan +`project.obp` files, and pairs the agent relay with the window you have open. + ## Automatic reverse engineering An AI agent can drive Reverse engineer from start to finish: give it a scan diff --git a/desktop/.gitignore b/desktop/.gitignore new file mode 100644 index 00000000..3a0bfc2c --- /dev/null +++ b/desktop/.gitignore @@ -0,0 +1,2 @@ +release/ +release-new/ diff --git a/desktop/README.md b/desktop/README.md new file mode 100644 index 00000000..a13e23ea --- /dev/null +++ b/desktop/README.md @@ -0,0 +1,45 @@ +# ScanRuler Desktop + +An Electron shell around the ScanRuler build in this repository: the app runs +from the local `dist/`, with no browser and no scanruler.com, paired with the +`scanruler-mcp` relay so agents reach the window you are looking at. + +```bash +# in the repository root: npm ci && npm --prefix mcp ci && npm run build +cd desktop +npm install +npm start # runs the app from the checkout +npm run dist # packages release/win-unpacked/ScanRuler.exe +npm test # smoke (isolation, threads, WebGL2, WebGPU) and the Creality watcher +node test/i18n.mjs # the page stays translated as React redraws +node test/e2e-agent.mjs # an MCP client reaches the page through the relay +``` + +Windows x64 only for now (`electron-builder --win dir`). + +## What it adds + +- **Graphics backend**: Help → Hardware picks the backend ANGLE translates WebGL + to — DirectX 11 (default), DirectX 12, Vulkan or OpenGL — and shows the GPU, + WebGPU, thread count, cross-origin isolation and WebNN. A change applies on + restart. +- **Language**: the ScanRuler page is translated at run time, node by node, from + `i18n/.json`, and kept translated as React redraws; the source of the + app is not touched. Today: `pt-BR`. The first language follows the OS; the + Language menu changes it. Text drawn on canvas and numbers built at run time + are not covered. +- **Creality Scan projects**: `project.obp` opens from the menu, the command + line, drag and drop or the file picker, resolving to the project's + `result/Mesh.ply` (or the newest session's `Fused.ply`). File → Watch Creality + Scan folder… offers each new `Mesh.ply` the scanner software writes. +- **Agents on the open window**: the app starts the relay itself and writes + `agent.json` (port and token) under its user-data folder; `scanruler-mcp.cmd` + next to the exe joins that app, so Claude Code or Codex drive the window the + user has open instead of a background copy: + + ```bash + claude mcp add --scope user scanruler -- "\release\win-unpacked\scanruler-mcp.cmd" + codex mcp add scanruler -- "\release\win-unpacked\scanruler-mcp.cmd" + ``` + +- File associations for `.scanruler`, `.stl`, `.ply`, `.obj`, `.3mf`, `.step` and `.obp`. diff --git a/desktop/agent-bridge.mjs b/desktop/agent-bridge.mjs new file mode 100644 index 00000000..5dba59ed --- /dev/null +++ b/desktop/agent-bridge.mjs @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Entry of scanruler-mcp.cmd: joins the ScanRuler app that is open — its port and token are +// kept in agent.json beside its settings — then runs the agent server. Without the app +// open (no file), or with SCANRULER_MCP_TOKEN / _PORT set by hand, nothing changes. +import { readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' + +const here = dirname(fileURLToPath(import.meta.url)) +try { + const a = JSON.parse(readFileSync(process.env.SCANRULER_AGENT_FILE ?? join(process.env.APPDATA ?? '', 'scanruler-desktop', 'agent.json'), 'utf8')) + process.env.SCANRULER_MCP_TOKEN ||= a.token + process.env.SCANRULER_MCP_PORT ||= String(a.port) +} catch {} +await import(pathToFileURL(join(here, 'mcp/bin/scanruler-mcp.js')).href) diff --git a/desktop/creality.js b/desktop/creality.js new file mode 100644 index 00000000..aa9cd95e --- /dev/null +++ b/desktop/creality.js @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Watches a folder of Creality Scan projects for a finished //result/Mesh.ply +// (the scan's final mesh) and says when one appears or changes, once its file stops growing. +import { watch, statSync, readFileSync, existsSync } from 'node:fs' +import { dirname, join, relative, sep } from 'node:path' + +const MESH = /[\\/]result[\\/]Mesh\.ply$/i +const SETTLE_MS = 1500 + +export const isMesh = (path) => MESH.test(path) + +/** Calls onMesh(path, project) for each Mesh.ply under `dir` once its size is steady; returns stop(). */ +export function watchMeshes(dir, onMesh, { settleMs = SETTLE_MS } = {}) { + const waiting = new Map() // path -> timer + const check = (path, last) => { + let size + try { + size = statSync(path).size + } catch { + return waiting.delete(path) // gone again before it settled + } + if (size > 0 && size === last) { + waiting.delete(path) + return onMesh(path, relative(dir, path).split(sep)[0]) + } + waiting.set(path, setTimeout(() => check(path, size), settleMs)) + } + const watcher = watch(dir, { recursive: true }, (_event, name) => { + if (!name) return + const path = `${dir}${sep}${name}` + if (!isMesh(path)) return + clearTimeout(waiting.get(path)) + waiting.set(path, setTimeout(() => check(path, -1), settleMs)) + }) + return () => { + watcher.close() + for (const t of waiting.values()) clearTimeout(t) + waiting.clear() + } +} + +/** The scan a project.obp stands for: its final Mesh.ply, else the newest session's Fused.ply. */ +export function meshOfProject(obp) { + const dir = dirname(obp) + const text = readFileSync(obp, 'utf8') + const data = /^data_dir:\s*(\S+)/m.exec(text)?.[1] + const final = data && join(dir, data, 'result', 'Mesh.ply') + if (final && existsSync(final)) return final + const sessions = [...text.matchAll(/^\s*-\s*(\S+)\s*$/gm)] + .map((m) => join(dir, m[1], 'result', 'Fused.ply')) + .filter((f) => existsSync(f)) + .sort((a, b) => statSync(b).mtimeMs - statSync(a).mtimeMs) + if (sessions.length) return sessions[0] + throw new Error('Este projeto do Creality Scan ainda não tem malha (Mesh.ply) nem scan fundido (Fused.ply).') +} diff --git a/desktop/hardware.html b/desktop/hardware.html new file mode 100644 index 00000000..676df497 --- /dev/null +++ b/desktop/hardware.html @@ -0,0 +1,48 @@ + + + + +ScanRuler — Hardware + + +

Hardware

+
+
+ + + +
+

+ diff --git a/desktop/i18n.js b/desktop/i18n.js new file mode 100644 index 00000000..ec3b09de --- /dev/null +++ b/desktop/i18n.js @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Runtime translation of the ScanRuler page: the English text the app draws is +// swapped for the dictionary's text, node by node, and kept in step as React +// redraws. The ScanRuler source is not touched. Text drawn on a canvas (3D +// labels) and text built from numbers at run time are not covered. +import { readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' + +const here = dirname(fileURLToPath(import.meta.url)) + +export const LANGUAGES = { 'pt-BR': 'Português (Brasil)', en: 'English' } + +/** The dictionary of a language, or null for English (the page as it is). */ +export function loadDict(lang) { + if (lang === 'en') return null + try { + return JSON.parse(readFileSync(join(here, 'i18n', `${lang}.json`), 'utf8')) + } catch { + return null + } +} + +/** A script that installs the translator in the page, or switches its dictionary. */ +export function injectScript(dict) { + return `(${engine.toString()})(${JSON.stringify(dict)})` +} + +function engine(dict) { + if (window.__srI18n) return window.__srI18n.set(dict) + const ATTRS = ['title', 'aria-label', 'placeholder', 'alt'] + const tracked = new Set() // WeakRef of {node, attr?, orig, mine} + const byTarget = new WeakMap() // node|element -> {text?: rec, attrs: {name: rec}} + let words = null + + const skip = (el) => !el || /^(SCRIPT|STYLE|TEXTAREA|CODE|PRE)$/.test(el.tagName) || el.isContentEditable || el.closest?.('[data-no-i18n]') + const split = (s) => [s.match(/^\s*/)[0], s.trim(), s.match(/\s*$/)[0]] + + function setText(node) { + if (skip(node.parentElement)) return + let rec = byTarget.get(node)?.text + if (rec && node.nodeValue === rec.mine) return // our own write + const [lead, core, tail] = split(node.nodeValue) + if (!core) return + const to = words?.[core] + if (!to) { if (rec) rec.mine = null; return } + if (!rec) { + rec = { ref: new WeakRef(node), kind: 'text' } + byTarget.set(node, { ...(byTarget.get(node) ?? { attrs: {} }), text: rec }) + tracked.add(rec) + } + rec.orig = node.nodeValue + rec.mine = lead + to + tail + node.nodeValue = rec.mine + } + + function setAttr(el, name) { + const cur = el.getAttribute(name) + if (cur == null) return + const entry = byTarget.get(el) ?? { attrs: {} } + byTarget.set(el, entry) + let rec = entry.attrs[name] + if (rec && cur === rec.mine) return + const to = words?.[cur.trim()] + if (!to) return + if (!rec) { rec = { ref: new WeakRef(el), kind: 'attr', name }; entry.attrs[name] = rec; tracked.add(rec) } + rec.orig = cur + rec.mine = to + el.setAttribute(name, to) + } + + function walk(root) { + if (!words) return + if (root.nodeType === Node.TEXT_NODE) return setText(root) + if (root.nodeType !== Node.ELEMENT_NODE) return + const w = document.createTreeWalker(root, NodeFilter.SHOW_TEXT) + for (let n; (n = w.nextNode()); ) setText(n) + const els = root.matches?.('[title],[aria-label],[placeholder],[alt]') ? [root] : [] + for (const el of [...els, ...root.querySelectorAll('[title],[aria-label],[placeholder],[alt]')]) for (const a of ATTRS) setAttr(el, a) + } + + function restore() { + for (const rec of tracked) { + const t = rec.ref.deref() + if (!t) { tracked.delete(rec); continue } + if (rec.kind === 'text' && t.nodeValue === rec.mine) t.nodeValue = rec.orig + if (rec.kind === 'attr' && t.getAttribute(rec.name) === rec.mine) t.setAttribute(rec.name, rec.orig) + rec.mine = null + } + } + + const observer = new MutationObserver((list) => { + for (const m of list) { + if (m.type === 'childList') m.addedNodes.forEach(walk) + else if (m.type === 'characterData') setText(m.target) + else if (m.type === 'attributes') setAttr(m.target, m.attributeName) + } + }) + observer.observe(document.body, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ATTRS }) + + window.__srI18n = { + set(next) { + restore() + words = next + if (words) walk(document.body) + }, + } + window.__srI18n.set(dict) +} diff --git a/desktop/i18n/pt-BR.json b/desktop/i18n/pt-BR.json new file mode 100644 index 00000000..228fd799 --- /dev/null +++ b/desktop/i18n/pt-BR.json @@ -0,0 +1,347 @@ +{ + "CNC Kitchen · 3D scan analysis ·": "CNC Kitchen · análise de scans 3D ·", + "3D Measure": "Medição 3D", + "Surface Deviation": "Desvio de superfície", + "Wall Thickness": "Espessura de parede", + "2D Measure": "Medição 2D", + "Reverse engineer": "Engenharia reversa", + "Mesh Tools": "Ferramentas de malha", + "No unsaved changes": "Sem alterações não salvas", + "New": "Novo", + "Project": "Projeto", + "Save": "Salvar", + "Load": "Carregar", + "Settings": "Ajustes", + "Model": "Modelo", + "No scan loaded": "Nenhum scan carregado", + "STL, PLY, OBJ, 3MF or STEP · mm": "STL, PLY, OBJ, 3MF ou STEP · mm", + "Open…": "Abrir…", + "Drop it anywhere in the window.": "Solte em qualquer lugar da janela.", + "Alignment": "Alinhamento", + "Auto-align": "Alinhar auto", + "Align part": "Alinhar peça", + "Move / rotate by numbers": "Mover / girar por valores", + "Create element": "Criar elemento", + "Point": "Ponto", + "Line": "Linha", + "Plane": "Plano", + "Sphere": "Esfera", + "Cylinder": "Cilindro", + "Cone": "Cone", + "Circle": "Círculo", + "Torus": "Toro", + "Section": "Seção", + "Create dimensions": "Criar cotas", + "New dimension": "Nova cota", + "New tolerance": "Nova tolerância", + "Fitting elements": "Ajuste de elementos", + "Load a scan, then pick features on it to fit spheres, cylinders and planes and measure between them. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Carregue um scan e selecione feições nele para ajustar esferas, cilindros e planos e medir entre eles. STL, PLY, OBJ, 3MF ou STEP, em milímetros — tudo fica neste aplicativo.", + "Scan": "Scan", + "The part as measured": "A peça como medida", + "Drop it here": "Solte aqui", + "Choose file…": "Escolher arquivo…", + "Drag & drop works anywhere in the window — the first empty slot takes it.": "Arrastar e soltar funciona em qualquer lugar da janela — o primeiro espaço vazio recebe o arquivo.", + "Thanks for using ScanRuler!": "Obrigado por usar o ScanRuler!", + "This tool is provided": "Esta ferramenta é oferecida", + "completely free": "totalmente grátis", + "by CNC Kitchen. Once you have finished measuring, why not have a look at the store that keeps us making things like this?": "pela CNC Kitchen. Quando terminar de medir, que tal conhecer a loja que nos mantém criando coisas como esta?", + "Visit CNCKitchen.STORE": "Visite a CNCKitchen.STORE", + "Send a tip on PayPal": "Envie uma gorjeta pelo PayPal", + "Send a tip on Ko-fi": "Envie uma gorjeta pelo Ko-fi", + "Transparent": "Transparente", + "Mesh": "Malha", + "Backfaces": "Faces traseiras", + "Elements": "Elementos", + "Labels": "Rótulos", + "READY": "PRONTO", + "IMPRINT": "AVISO LEGAL", + "LOCAL ONLY": "SOMENTE LOCAL", + "· FILES NEVER LEAVE YOUR BROWSER": "· OS ARQUIVOS NUNCA SAEM DO SEU APLICATIVO", + "Units of": "Unidades de", + "An STL file carries no units — a 1 in it is whatever the program that wrote it meant. Say what this one is in and it is read in millimetres, like everything measured here.": "Um arquivo STL não traz unidades — um 1 nele vale o que o programa que o gravou quis dizer. Informe a unidade deste e ele será lido em milímetros, como tudo o que é medido aqui.", + "Coordinates in": "Coordenadas em", + "Millimetres": "Milímetros", + "Centimetres": "Centímetros", + "Metres": "Metros", + "Inches": "Polegadas", + "Don't ask again": "Não perguntar de novo", + "— read every STL in these units. Settings brings the question back.": "— ler todo STL nestas unidades. Os Ajustes trazem a pergunta de volta.", + "Cancel": "Cancelar", + "Open": "Abrir", + "Your data will no longer stay on this computer": "Seus dados deixarão de ficar neste computador", + "Without an agent, everything stays in this browser. With one connected, what the agent reads — measurements, reports, pictures of the part, file names, notes, and any file it opens itself — is processed by the agent and the AI model behind it.": "Sem um agente, tudo fica neste aplicativo. Com um conectado, o que o agente lê — medições, relatórios, imagens da peça, nomes de arquivos, anotações e qualquer arquivo que ele mesmo abrir — é processado pelo agente e pelo modelo de IA por trás dele.", + "Unless that model runs on this computer, all of this goes to the model’s provider": "A menos que esse modelo rode neste computador, tudo isso vai para o provedor do modelo", + ", under that provider’s terms.": ", sob os termos desse provedor.", + "ScanRuler itself sends nothing anywhere: the connection only reaches the agent’s helper on this computer. Don’t connect an agent while you work on parts you may not share with its provider.": "O próprio ScanRuler não envia nada a lugar nenhum: a conexão só alcança o auxiliar do agente neste computador. Não conecte um agente enquanto trabalhar com peças que você não pode compartilhar com o provedor dele.", + "Asked once in this browser. Settings → Agents switches the connection off at any time.": "Perguntado uma vez neste aplicativo. Ajustes → Agentes desliga a conexão a qualquer momento.", + "Connect the agent": "Conectar o agente", + "3D Measure\nFit spheres, cylinders and planes, and measure between them": "Medição 3D\nAjuste esferas, cilindros e planos e meça entre eles", + "Surface Deviation\nBest-fit the scan to a nominal part and map the difference": "Desvio de superfície\nAjuste o scan a uma peça nominal e mapeie a diferença", + "Wall Thickness\nMap the wall thickness of the part itself — no reference needed": "Espessura de parede\nMapeie a espessura de parede da própria peça — sem precisar de referência", + "2D Measure\nMeasure a flatbed scan the way a measuring microscope would": "Medição 2D\nMeça um scan de mesa como faria um microscópio de medição", + "Reverse engineer\nSketch on sections, extrude and revolve, and export a solid STEP body built over the scan": "Engenharia reversa\nFaça esboços sobre seções, extruse e revolucione, e exporte um corpo sólido STEP construído sobre o scan", + "Mesh Tools\nClean the scan up — islands, holes, scatter — expand, shrink or hollow it, and reduce it to a light mesh for CAD held within a tolerance you set": "Ferramentas de malha\nLimpe o scan — ilhas, furos, ruído — expanda, reduza ou esvazie, e simplifique-o em uma malha leve para CAD dentro de uma tolerância definida por você", + "The project's name — what Save Project saves it under and every export is named on. Unnamed, they go by the scan or the image it holds.": "O nome do projeto — com o qual Salvar projeto o grava e que dá nome a cada exportação. Sem nome, usam o do scan ou da imagem que ele contém.", + "Project name": "Nome do projeto", + "Untitled project": "Projeto sem título", + "Checkpoints are stored only in this browser. Checkpoints and project files include completed work; finish active drawings before leaving. Save Project downloads a portable copy.": "Os pontos de restauração ficam guardados apenas neste aplicativo. Pontos de restauração e arquivos de projeto incluem o trabalho concluído; termine os desenhos em andamento antes de sair. Salvar projeto baixa uma cópia portátil.", + "Undo — Ctrl+Z": "Desfazer — Ctrl+Z", + "Undo": "Desfazer", + "Redo — Ctrl+Y / Ctrl+Shift+Z": "Refazer — Ctrl+Y / Ctrl+Shift+Z", + "Redo": "Refazer", + "Start a new project: the scan, the reference and the image closed, every measurement and every workspace's work let go, the name taken off — after asking, when there are changes not saved": "Iniciar um novo projeto: scan, referência e imagem fechados, todas as medições e o trabalho de cada área de trabalho descartados, nome removido — com confirmação, quando houver alterações não salvas", + "Save the scan, the reference, the image and every measurement as one .scanruler file": "Salvar o scan, a referência, a imagem e todas as medições em um único arquivo .scanruler", + "Open a .scanruler project — or a plain scan or image to start fresh": "Abrir um projeto .scanruler — ou um scan ou imagem simples para começar do zero", + "Interface theme, colour mode, mouse controls, line widths and guided hints": "Tema da interface, modo de cor, controles do mouse, espessuras de linha e dicas guiadas", + "Source code of the open-source core (AGPL-3.0-only) on GitHub": "Código-fonte do núcleo de código aberto (AGPL-3.0-only) no GitHub", + "More about The scan": "Mais sobre o scan", + "More about Alignment": "Mais sobre o alinhamento", + "More about Elements": "Mais sobre os elementos", + "Cut the scan with a plane, to measure the cut in the 2D Measure workspace": "Cortar o scan com um plano, para medir o corte na área de Medição 2D", + "More about Dimensions": "Mais sobre as cotas", + "More about Geometric tolerances": "Mais sobre tolerâncias geométricas", + "Fit to view — bring the whole model back on screen, without turning it": "Ajustar à vista — traz o modelo inteiro de volta à tela, sem girá-lo", + "Fit to view": "Ajustar à vista", + "Close": "Fechar", + "Load a scan first": "Carregue um scan primeiro", + "How the scan is drawn": "Como o scan é desenhado", + "Draw the scan transparent — what lies inside it, the fitted elements and the pinned readings show instead of hiding behind the surface in front of them": "Desenha o scan transparente — o que está dentro dele, os elementos ajustados e as leituras fixadas aparecem em vez de ficarem atrás da superfície à frente", + "Draw the triangle edges on the scan, to see how fine it is and where it is patchy. The mesh comes up as you zoom in: from a distance the triangles are smaller than a pixel, and the edges fade out rather than turning the part black": "Desenha as arestas dos triângulos no scan, para ver quão fino ele é e onde é irregular. A malha aparece conforme você aproxima: de longe os triângulos são menores que um pixel, e as arestas somem em vez de escurecer a peça", + "Colour the far side of every triangle — holes in the scan and inverted normals stop looking like solid part": "Colore o lado oposto de cada triângulo — furos no scan e normais invertidas deixam de parecer peça sólida", + "Nothing measured yet — fit an element or cut a section and it shows here": "Nada medido ainda — ajuste um elemento ou corte uma seção e aparecerá aqui", + "How the elements are drawn": "Como os elementos são desenhados", + "Show the name tags and readouts on the part. The fitted elements, the sections and the dimension lines stay either way": "Mostra os rótulos de nome e as leituras sobre a peça. Os elementos ajustados, as seções e as linhas de cota permanecem de qualquer forma", + "Cut the parts open at a plane to look inside — the scan, the reference and everything measured on them. Closed parts are closed where they are cut; an open scan stays a shell. Slide the plane with the arrow, tilt it with the rings": "Corta as peças em um plano para ver o interior — o scan, a referência e tudo o que foi medido neles. Peças fechadas são fechadas onde são cortadas; um scan aberto continua uma casca. Deslize o plano com a seta, incline-o com os anéis", + "Measurement": "Medição", + "Method": "Método", + "Ray along the normal": "Raio ao longo da normal", + "Sphere across the wall": "Esfera através da parede", + "Max. thickness": "Espessura máx.", + "Rays": "Raios", + "Single ray — fast": "Raio único — rápido", + "6-ray cone — tighter": "Cone de 6 raios — mais preciso", + "12-ray cone — tightest": "Cone de 12 raios — o mais preciso", + "Far surface must face back": "Superfície oposta deve estar voltada para trás", + "Max. deviation of normals": "Desvio máx. das normais", + "Measure wall thickness": "Medir espessura de parede", + "Everything above shapes the search, so changing it means measuring again.": "Tudo acima define a busca, então alterar qualquer item exige medir de novo.", + "Wall thickness": "Espessura de parede", + "Load a scan and measure how thick its walls are, everywhere at once. No reference model, no alignment. STL, PLY, OBJ, 3MF or STEP, in millimetres — everything stays in this browser.": "Carregue um scan e meça a espessura de suas paredes, em todos os pontos de uma vez. Sem modelo de referência, sem alinhamento. STL, PLY, OBJ, 3MF ou STEP, em milímetros — tudo fica neste aplicativo.", + "More about How thickness is measured": "Mais sobre como a espessura é medida", + "More about Max. thickness": "Mais sobre a espessura máx.", + "More about Rays": "Mais sobre os raios", + "More about Far surface must face back": "Mais sobre a superfície oposta voltada para trás", + "More about Max. deviation of normals": "Mais sobre o desvio máx. das normais", + "Image": "Imagem", + "No image loaded": "Nenhuma imagem carregada", + "PNG or JPEG": "PNG ou JPEG", + "Measuring a flatbed scan": "Medindo um scan de mesa", + "Scan the part face-down on a flatbed scanner and open the image — then fit points, lines and circles to its edges and measure between them, the way a measuring microscope does. PNG or JPEG, at the highest optical resolution you have. Everything stays in this browser.": "Escaneie a peça virada para baixo em um scanner de mesa e abra a imagem — depois ajuste pontos, linhas e círculos às bordas dela e meça entre eles, como faz um microscópio de medição. PNG ou JPEG, na maior resolução óptica que você tiver. Tudo fica neste aplicativo.", + "The flatbed scan of the part": "O scan de mesa da peça", + "More about What is measured": "Mais sobre o que é medido", + "CAD kernel ready — OCCT 8.0.1 via occt-wasm 5.7.1.": "Kernel CAD pronto — OCCT 8.0.1 via occt-wasm 5.7.1.", + "Parameters": "Parâmetros", + "+ Add": "+ Adicionar", + "Create feature": "Criar recurso", + "Sketch on": "Esboço em", + "Choose a plane…": "Escolher um plano…", + "Extrude": "Extrusar", + "Revolve": "Revolucionar", + "Extract": "Extrair", + "Import STEP": "Importar STEP", + "Shell": "Casca", + "Fillet": "Arredondamento", + "Chamfer": "Chanfro", + "Draft": "Ângulo de saída", + "Move face": "Mover face", + "Delete face": "Excluir face", + "Hole": "Furo", + "Thread": "Rosca", + "Cut by plane": "Cortar por plano", + "Mirror": "Espelhar", + "Combine": "Combinar", + "Sheet": "Folha", + "Split": "Dividir", + "Loft": "Loft", + "Sweep": "Varredura", + "Pattern": "Padrão", + "Move body": "Mover corpo", + "Offset body": "Deslocar corpo", + "Fit surface": "Ajustar superfície", + "Patch network": "Rede de remendos", + "Trim": "Aparar", + "Thicken": "Espessar", + "Start with a sketch: choose a coordinate plane above. No scan is needed to model.": "Comece com um esboço: escolha um plano de coordenadas acima. Não é preciso scan para modelar.", + "Bodies": "Corpos", + "XY plane": "Plano XY", + "XZ plane": "Plano XZ", + "YZ plane": "Plano YZ", + "Curvature": "Curvatura", + "Overlaps": "Sobreposições", + "Tangent edges": "Arestas tangentes", + "Drawn": "Desenhadas", + "Dimmed": "Atenuadas", + "Off": "Desligado", + "Deviation": "Desvio", + "More about Reverse engineering": "Mais sobre engenharia reversa", + "Show the parameters": "Mostrar os parâmetros", + "More about Parameters": "Mais sobre os parâmetros", + "Add a parameter": "Adicionar um parâmetro", + "More about Features": "Mais sobre os recursos", + "Extrude a sketch's regions — pick them in the viewport": "Extrusa as regiões de um esboço — selecione-as na vista", + "Revolve a sketch's regions about an axis": "Revoluciona as regiões de um esboço em torno de um eixo", + "A measured plane, cylinder, cone or sphere as a body at its extents, extended as in 3D Measure — a boss or a pin without a sketch — or a shadow section as its block, the outline through the part's thickness": "Um plano, cilindro, cone ou esfera medido como corpo em suas extensões, estendido como na Medição 3D — um ressalto ou pino sem esboço — ou uma seção de sombra como seu bloco, o contorno através da espessura da peça", + "Bring a STEP file in as bodies of its own — a bought-in part, a mating part, the CAD model to check the scan against. A body for every solid in the file, named as the file names its parts; Move body puts them where they belong": "Traz um arquivo STEP como corpos próprios — uma peça comprada, uma peça de encaixe, o modelo CAD para conferir o scan. Um corpo para cada sólido do arquivo, nomeado como o arquivo nomeia suas peças; Mover corpo os coloca onde devem ficar", + "Hollow the body to a wall — click the faces to open, or none for a closed hollow; the wall comes from the Thickness map when one was measured": "Esvazia o corpo até uma parede — clique nas faces a abrir, ou em nenhuma para um oco fechado; a parede vem do mapa de espessura, quando um foi medido", + "Round edges of the body — click them; the radius is read off the scan across the first": "Arredonda arestas do corpo — clique nelas; o raio é lido do scan na primeira", + "Chamfer edges of the body — click them": "Chanfra arestas do corpo — clique nelas", + "Lean faces of the body about a neutral plane — a moulding's draft, put on after the fact: click the flat face that stays, then the faces to lean": "Inclina faces do corpo em torno de um plano neutro — o ângulo de saída de uma moldagem, aplicado depois: clique na face plana que fica, depois nas faces a inclinar", + "Offset faces of the body by a distance — a flat face along its normal, a round face in radius; several picked together grow or shrink as a whole, a cylinder's wall and its ends. One face can be snapped onto the scan instead: pick it, take Snap to scan, and click the scan where it should lie": "Desloca faces do corpo por uma distância — uma face plana ao longo da normal, uma face redonda no raio; várias selecionadas juntas crescem ou encolhem como um todo, a parede de um cilindro e suas extremidades. Uma face pode, em vez disso, ser encaixada no scan: selecione-a, use Encaixar no scan e clique no scan onde ela deve ficar", + "Delete faces of the body and heal the gap — a fillet, a chamfer, a hole or a boss taken out, the faces round it run on to close over where it was: click a face of it, and the rest of the round or hole comes with it": "Exclui faces do corpo e fecha a lacuna — um arredondamento, chanfro, furo ou ressalto removido, as faces ao redor se prolongam para cobrir onde estava: clique numa face dele e o restante do arredondamento ou furo vai junto", + "A hole through the body along a cylinder or circle element, or a round face of the body": "Um furo através do corpo ao longo de um elemento cilindro ou círculo, ou de uma face redonda do corpo", + "A modeled screw thread on a round face of the body — a bolt's on a boss, a nut's in a bore: ISO metric, UNC or UNF, a 45° form that prints without supports, or a diameter and pitch of your own, with an offset for clearance when printing": "Uma rosca modelada em uma face redonda do corpo — a de um parafuso em um ressalto, a de uma porca em um furo: métrica ISO, UNC ou UNF, um perfil de 45° que imprime sem suportes, ou um diâmetro e passo seus, com uma folga de compensação para impressão", + "Cut the body at a plane and keep one side": "Corta o corpo em um plano e mantém um dos lados", + "Mirror the body about a plane, joined with the source": "Espelha o corpo em relação a um plano, unido ao original", + "Join, cut or intersect two bodies made separately — or trim a sheet by a solid, or sew two sheets into one, before Thicken": "Une, corta ou interseciona dois corpos feitos separadamente — ou apara uma folha por um sólido, ou costura duas folhas em uma, antes de Espessar", + "A sketch's pieces — an open chain will do — extruded as a surface: a cutter to split a body with": "As partes de um esboço — uma cadeia aberta serve — extrusadas como superfície: um cortador para dividir um corpo", + "Split a body by a sheet and keep the fragments that lie on the scan — or trim a sheet by sheets or solids, keeping its pieces on the scan": "Divide um corpo por uma folha e mantém os fragmentos que ficam sobre o scan — ou apara uma folha por folhas ou sólidos, mantendo suas partes sobre o scan", + "A solid through the profiles of two or more sketches on their own planes": "Um sólido através dos perfis de dois ou mais esboços em seus próprios planos", + "A sketch's region swept along a chain of lines and arcs in another sketch — a bent bar, a handle, a rim that follows a curve": "A região de um esboço varrida ao longo de uma cadeia de linhas e arcos em outro esboço — uma barra dobrada, uma alça, uma borda que segue uma curva", + "Copies of the body about an axis, along a direction, or in a grid": "Cópias do corpo em torno de um eixo, ao longo de uma direção ou em grade", + "Move and turn bodies — the ones selected on the part or picked in the body list (Ctrl+click or Shift+click rows for several), or click them on the part: drag the gizmo's arrows and rings, or type the distances and angles. Tick Copy to move a copy and leave the body where it is": "Move e gira corpos — os selecionados na peça ou escolhidos na lista de corpos (Ctrl+clique ou Shift+clique nas linhas para vários), ou clique neles na peça: arraste as setas e anéis do gizmo, ou digite as distâncias e ângulos. Marque Copiar para mover uma cópia e deixar o corpo onde está", + "Grow a body all round by a distance, or shrink it — kept beside it as a body of its own, a keep-out round a part or the pocket that holds it with clearance": "Aumenta um corpo por igual em todos os lados por uma distância, ou o reduz — mantido ao lado como um corpo próprio, uma zona de exclusão ao redor de uma peça ou o bolsão que a abriga com folga", + "A smooth freeform surface fitted through a region of the scan — click faces or mark them — for the trim to close into a solid with the others": "Uma superfície livre e suave ajustada a uma região do scan — clique em faces ou marque-as — para o aparo fechar em um sólido com as demais", + "Curves drawn on the scan, and the regions they close filled with patches fitted to the scan together — tangent across every curve not marked a crease, sharing their edges: a sheet, or a solid where the network closes. For a freeform skin that folds back or wants more than one patch": "Curvas desenhadas sobre o scan, e as regiões que elas fecham preenchidas com remendos ajustados ao scan em conjunto — tangentes em cada curva não marcada como vinco, compartilhando suas arestas: uma folha, ou um sólido onde a rede fecha. Para uma pele livre que se dobra sobre si ou precisa de mais de um remendo", + "Close a set of fitted surfaces, sheets and measured planes, cylinders, cones and spheres into a solid: the faces they cut space into are kept where they lie on the scan and sewn": "Fecha um conjunto de superfícies ajustadas, folhas e planos, cilindros, cones e esferas medidos em um sólido: as faces em que dividem o espaço são mantidas onde ficam sobre o scan e costuradas", + "Make a sheet or a fitted surface a solid of a thickness — a freeform thin shell's wall from its one skin, where a shell or an offset of a lofted body will not go": "Transforma uma folha ou superfície ajustada em um sólido de determinada espessura — a parede de uma casca fina livre a partir de sua única pele, onde uma casca ou um deslocamento de um corpo em loft não funciona", + "The XY plane through the origin — click it in the viewport to sketch on it": "O plano XY pela origem — clique nele na vista para esboçar sobre ele", + "Hide the XY plane": "Ocultar o plano XY", + "The XZ plane through the origin — click it in the viewport to sketch on it": "O plano XZ pela origem — clique nele na vista para esboçar sobre ele", + "Hide the XZ plane": "Ocultar o plano XZ", + "The YZ plane through the origin — click it in the viewport to sketch on it": "O plano YZ pela origem — clique nele na vista para esboçar sobre ele", + "Hide the YZ plane": "Ocultar o plano YZ", + "Load a scan first — the map reads its surface": "Carregue um scan primeiro — o mapa lê a superfície dele", + "Build a body first": "Construa um corpo primeiro", + "How the bodies are drawn": "Como os corpos são desenhados", + "Draw the bodies transparent — the scan, the elements and the bodies inside them show through. With the scan solid as well, each part shows where it stands proud of the other": "Desenha os corpos transparentes — o scan, os elementos e os corpos dentro deles aparecem através. Com o scan sólido também, cada peça mostra onde se projeta além da outra", + "Draw where two bodies run into each other in hatched red — what they share, over the faces of both that would otherwise fight for the same pixels. Off for bodies that overlap on purpose, a modifier volume for a slicer; saved with the project": "Desenha em vermelho hachurado onde dois corpos se interpenetram — o que compartilham, sobre as faces de ambos que de outra forma disputariam os mesmos pixels. Desligue para corpos que se sobrepõem de propósito, um volume modificador para um fatiador; salvo com o projeto", + "How the edges are drawn where a round runs into a flat. A round face's seam and the cut a join leaves across one surface are never drawn": "Como as arestas são desenhadas onde uma face redonda encontra uma plana. A emenda de uma face redonda e o corte que uma junção deixa numa superfície nunca são desenhados", + "Tangent edges drawn — where a round runs into a flat — as the sharp ones are, the way CAD draws them": "Arestas tangentes desenhadas — onde uma face redonda encontra uma plana — como as vivas, do jeito que o CAD as desenha", + "Tangent edges dimmed": "Arestas tangentes atenuadas", + "Tangent edges left out, and not picked: a round shows by its outline against the background": "Arestas tangentes omitidas e não selecionáveis: uma face redonda aparece pelo contorno contra o fundo", + "Nothing measured yet — fit elements or cut sections in the 3D Measure workspace and they show here": "Nada medido ainda — ajuste elementos ou corte seções na área de Medição 3D e aparecerão aqui", + "Build a body first — the map reads the scan against it": "Construa um corpo primeiro — o mapa lê o scan em relação a ele", + "Tools": "Ferramentas", + "Reduce mesh": "Reduzir malha", + "Remove islands": "Remover ilhas", + "Delete marked": "Excluir marcados", + "Flip normals": "Inverter normais", + "Cut": "Cortar", + "Fill holes": "Preencher furos", + "Smooth": "Suavizar", + "Shrink/expand": "Reduzir/expandir", + "Boolean": "Booleana", + "Trim to scan": "Aparar ao scan", + "Shell/hollow": "Casca/esvaziar", + "Tolerance": "Tolerância", + "Triangle limit": "Limite de triângulos", + "Reduce": "Reduzir", + "Both figures shape the reduction, so changing them means reducing again.": "Os dois valores definem a redução, então alterá-los exige reduzir de novo.", + "Cleaning up and reducing a scan": "Limpando e reduzindo um scan", + "Load a scan and clean it up — clear the stray islands, delete what is not the part, turn it the right way out, cut off the base it stood on, fill its holes, smooth out the scatter, expand or shrink it, hollow it — and reduce it to a light mesh to model over in CAD, held within a tolerance you set, measured against the scan to prove it, and exported where you aligned it. STL, PLY, OBJ or 3MF, in millimetres — everything stays in this browser.": "Carregue um scan e limpe-o — remova as ilhas soltas, apague o que não é a peça, vire-o para o lado certo, corte a base em que estava apoiado, preencha os furos, suavize o ruído, expanda ou reduza, esvazie — e simplifique-o em uma malha leve para modelar sobre ela no CAD, dentro de uma tolerância definida por você, medida contra o scan para comprovar, e exportada onde você a alinhou. STL, PLY, OBJ ou 3MF, em milímetros — tudo fica neste aplicativo.", + "Reduced": "Reduzida", + "Accuracy": "Precisão", + "More about Mesh tools": "Mais sobre as ferramentas de malha", + "Mesh tool": "Ferramenta de malha", + "A light copy of the scan to model over in CAD, held within a tolerance you set — the scan itself stays as it is": "Uma cópia leve do scan para modelar sobre ela no CAD, dentro de uma tolerância definida por você — o próprio scan permanece como está", + "Find the specks floating off the scan — every piece joined to nothing else and smaller than a size you set — mark them on the part, and delete them once you say so": "Encontra os fragmentos soltos do scan — cada pedaço sem ligação com o resto e menor que um tamanho definido por você — marca-os na peça e os exclui quando você mandar", + "Mark surface on the part by hand — a window, a brush or a lasso — and delete it from the scan: a fixture, a clamp, a patch of the table": "Marque superfície na peça à mão — uma janela, um pincel ou um laço — e exclua-a do scan: um gabarito, uma presilha, um pedaço da mesa", + "Turn the surface over so it faces the other way — the whole part, exported inside-out, or a patch you mark that faces in": "Vira a superfície para que fique voltada para o outro lado — a peça inteira, exportada do avesso, ou um trecho marcado que está voltado para dentro", + "Cut the scan with a plane and take one side off — the turntable it stood on, or half the part — closing it flat where the plane went through": "Corta o scan com um plano e remove um dos lados — o prato giratório em que estava, ou metade da peça — fechando-o plano onde o plano passou", + "Close the holes a scanner left, every one smaller than a size you set, with patches that carry the surface across": "Fecha os furos deixados pelo scanner, todos menores que um tamanho definido por você, com remendos que continuam a superfície", + "Take the scanner’s scatter out of the surface, never moving any point further than a limit you set, sharp edges held": "Remove o ruído do scanner da superfície, sem mover nenhum ponto além de um limite definido por você, mantendo as arestas vivas", + "Move the surface along its normal by a distance — out or in, the whole part or a marked region — not a scale: a bore gets narrower as the part grows": "Move a superfície ao longo da normal por uma distância — para fora ou para dentro, a peça inteira ou uma região marcada — não é escala: um furo fica mais estreito conforme a peça cresce", + "Take a solid body out of the scan, add it, or keep what the two share — a bore the scanner could not see to the bottom of, cut out with a cylinder modelled in Reverse engineer": "Subtrai um corpo sólido do scan, soma-o, ou mantém o que os dois compartilham — um furo cujo fundo o scanner não alcançou, cortado com um cilindro modelado em Engenharia reversa", + "Cut a body modelled to mend a broken part to fit the scanned break — the missing piece, to print and glue in": "Corta um corpo modelado para consertar uma peça quebrada de modo a encaixar na quebra escaneada — a peça faltante, para imprimir e colar", + "Hollow a closed part, leaving a wall of a thickness you set round a sealed cavity — or thicken an open surface into a solid": "Esvazia uma peça fechada, deixando uma parede de espessura definida por você em torno de uma cavidade selada — ou espessa uma superfície aberta até virar um sólido", + "More about Reducing a scan for CAD": "Mais sobre reduzir um scan para CAD", + "More about Tolerance": "Mais sobre a tolerância", + "More about Triangle limit": "Mais sobre o limite de triângulos", + "What to show of the reduction": "O que mostrar da redução", + "Reduce the scan first — the view keys show the result": "Reduza o scan primeiro — as teclas de vista mostram o resultado", + "Edit the scan first — the deviation shows how far the edited copy is from the scan in use": "Edite o scan primeiro — o desvio mostra o quanto a cópia editada se afasta do scan em uso", + "How the instrument looks and behaves. Remembered in this browser — nothing here is a property of the part, and none of it is saved with a project.": "Como o instrumento se apresenta e se comporta. Lembrado neste aplicativo — nada aqui é propriedade da peça, e nada é salvo com o projeto.", + "Units": "Unidades", + "Lengths in": "Comprimentos em", + "What is shown and typed. Files, projects and agent answers stay in millimetres.": "O que é exibido e digitado. Arquivos, projetos e respostas de agentes permanecem em milímetros.", + "Appearance": "Aparência", + "Interface": "Interface", + "Light": "Claro", + "Dark": "Escuro", + "System": "Sistema", + "Light or dark chassis.": "Chassi claro ou escuro.", + "follows the operating system's setting, and changes with it.": "segue a configuração do sistema operacional e muda junto com ela.", + "Colour mode": "Modo de cor", + "Studio grey": "Cinza estúdio", + "Scanner blue": "Azul scanner", + "Glossy blue under a hard light — surface detail stands out": "Azul brilhante sob luz dura — os detalhes da superfície se destacam", + ". What has been measured keeps its colour either way — the element tints and the deviation ramp are the same in both.": ". O que foi medido mantém a cor de qualquer forma — as cores dos elementos e a rampa de desvio são as mesmas nos dois.", + "Colour map": "Mapa de cores", + "Blue through cyan, green and yellow to red, a saturated green in the middle — the inspection-software classic. Uneven in lightness, so cyan and yellow show as bands the part does not have": "Do azul ao vermelho passando por ciano, verde e amarelo, com um verde saturado no meio — o clássico dos softwares de inspeção. Irregular em luminosidade, então ciano e amarelo aparecem como faixas que a peça não tem", + ". Every map painted on the part, and the scale beside it, is drawn in it; the block at either end is what a reading past that end of the scale wears.": ". Todo mapa pintado na peça, e a escala ao lado, é desenhado com ele; o bloco em cada extremidade é a cor de uma leitura além dessa extremidade da escala.", + "Sharp edges": "Arestas vivas", + "Always sharp": "Sempre vivas", + "Auto — sharp on a CAD-like mesh": "Auto — vivas em malha tipo CAD", + "Always smooth": "Sempre suaves", + "Sharp from": "Viva a partir de", + "Whether an edge of the scan is shaded as a crease, each face with its own normal, or smoothed across so a box looks pillowed — and from what angle between its two faces an edge counts as sharp.": "Se uma aresta do scan é sombreada como um vinco, cada face com sua própria normal, ou suavizada de lado a lado, de modo que uma caixa pareça almofadada — e a partir de que ângulo entre as duas faces uma aresta conta como viva.", + "draws a CAD mesh's faces flat and a scan's real edges — the rim of a bore, a step — as edges; noise seldom tips a scan's triangles that far, and if it still shows, a larger angle, 50° or 60°, leaves it smooth.": "desenha as faces de uma malha CAD planas e as arestas reais de um scan — a borda de um furo, um degrau — como arestas; o ruído raramente inclina os triângulos de um scan tanto assim, e se ainda aparecer, um ângulo maior, 50° ou 60°, a deixa suave.", + "Auto": "Auto", + "draws the creases only on a mesh that reads as a tessellation of CAD, a scan smooth all over. A reference part is always drawn sharp, from": "desenha os vincos apenas em uma malha que parece uma tesselação de CAD, e um scan liso por inteiro. Uma peça de referência é sempre desenhada com arestas vivas, a partir de", + "Navigation": "Navegação", + "Mouse controls": "Controles do mouse", + "left-drag orbit · right-drag pan · scroll zoom": "arrastar esquerdo: órbita · arrastar direito: pan · rolagem: zoom", + "works beside any of these, in Chrome and Edge: push or pull the puck to zoom, slide it to pan, tilt, twist and roll it to turn the part. It connects the first time you touch it, and drives the view the mouse was last over.": "funciona junto com qualquer um destes, no Chrome e no Edge: empurre ou puxe o botão para dar zoom, deslize para mover, incline, gire e role para girar a peça. Conecta na primeira vez que você o toca e controla a vista sobre a qual o mouse esteve por último.", + "Files": "Arquivos", + "Ask what units an STL is in": "Perguntar em que unidades está um STL", + "— the format carries none, and a part read at the wrong scale measures wrong in every number after. Off, every STL is read in the units below without asking.": "— o formato não traz nenhuma, e uma peça lida na escala errada mede errado em todos os números seguintes. Desligado, todo STL é lido nas unidades abaixo sem perguntar.", + "STL import units": "Unidades de importação de STL", + "What an STL is read in when the question is off, and the answer the question offers first — the last one given. A PLY or OBJ is always read in millimetres; a STEP file says its own.": "A unidade em que um STL é lido quando a pergunta está desligada, e a resposta que a pergunta oferece primeiro — a última dada. Um PLY ou OBJ é sempre lido em milímetros; um arquivo STEP informa a sua própria.", + "Lines": "Linhas", + "Section cuts": "Cortes de seção", + "How heavy a section's cut is drawn on the part in 3D Measure. The curves measured on its sheet follow in proportion.": "Quão grossa é desenhada a linha de corte de uma seção na peça, na Medição 3D. As curvas medidas em sua folha acompanham em proporção.", + "2D fitted curves": "Curvas ajustadas 2D", + "The lines, circles and splines fitted over a flatbed scan in 2D Measure, and the callouts drawn with them.": "As linhas, círculos e splines ajustados sobre um scan de mesa na Medição 2D, e as chamadas desenhadas com eles.", + "2D edges": "Arestas 2D", + "The edge chains the curves are fitted to — found in the scan, or cut by a section.": "As cadeias de arestas às quais as curvas são ajustadas — encontradas no scan ou cortadas por uma seção.", + "Guidance": "Orientação", + "Guided hints": "Dicas guiadas", + "— ring the control to press next, until you have been through a workspace twice. Switching it back on starts the guidance over.": "— destaca o controle a pressionar em seguida, até você ter passado duas vezes por uma área de trabalho. Reativar reinicia a orientação.", + "Agents": "Agentes", + "Local agent connection": "Conexão com agente local", + "— let an AI agent on this computer (Claude Code, Claude Desktop, Cursor …) drive this tab through its ScanRuler tools, the scanruler-mcp server. You watch every step here and can undo it.": "— permite que um agente de IA neste computador (Claude Code, Claude Desktop, Cursor …) controle esta janela por meio de suas ferramentas do ScanRuler, o servidor scanruler-mcp. Você acompanha cada passo aqui e pode desfazê-lo.", + "Your data no longer stays on this computer:": "Seus dados deixam de ficar neste computador:", + "what the agent reads — measurements, reports, pictures of the part, file names, notes — is processed by the agent and goes to its model’s provider, unless the model runs on this computer. ScanRuler itself sends nothing anywhere; the connection is to 127.0.0.1.": "o que o agente lê — medições, relatórios, imagens da peça, nomes de arquivos, anotações — é processado pelo agente e vai para o provedor do modelo, a menos que o modelo rode neste computador. O próprio ScanRuler não envia nada a lugar nenhum; a conexão é com 127.0.0.1.", + "Port": "Porta", + "Token": "Token", + "prints the port and the token, and a link that fills them in.": "exibe a porta e o token, e um link que os preenche.", + "Support": "Apoio", + "Support card": "Cartão de apoio", + "— the thank-you in the corner of the stage, with the ways to give something back. Its × closes it for this visit; switched off here it stays away, the big one below with it.": "— o agradecimento no canto da tela, com as formas de retribuir. O × o fecha nesta visita; desligado aqui, ele não aparece, e o grande abaixo também não.", + "Big card on saving": "Cartão grande ao salvar", + "— the same thank-you in the middle of the screen after a save or an export, at most once in ten minutes. The file does not wait for it. Its \"Don't show this again for a week\" shows here as off until the week is over.": "— o mesmo agradecimento no meio da tela após salvar ou exportar, no máximo uma vez a cada dez minutos. O arquivo não espera por ele. O \"Não mostrar de novo por uma semana\" dele aparece aqui como desligado até a semana acabar.", + "Dark blue through teal and green to yellow. Perceptually uniform — an even step in the reading is an even step in colour — and as legible to colour-blind eyes and in greyscale": "Azul escuro passando por azul-petróleo e verde até amarelo. Perceptualmente uniforme — um passo igual na leitura é um passo igual na cor — e igualmente legível para daltônicos e em escala de cinza", + "Black through purple and pink to pale cream. Perceptually uniform and colour-blind safe": "Preto passando por roxo e rosa até creme claro. Perceptualmente uniforme e seguro para daltônicos", + "Indigo through magenta and orange to yellow — no black end. Perceptually uniform and colour-blind safe": "Índigo passando por magenta e laranja até amarelo — sem extremidade preta. Perceptualmente uniforme e seguro para daltônicos", + "Black through purple, red and orange to pale yellow. Perceptually uniform and colour-blind safe": "Preto passando por roxo, vermelho e laranja até amarelo claro. Perceptualmente uniforme e seguro para daltônicos", + "Navy through grey to yellow, made to look the same to red–green colour blindness as to anyone else. Perceptually uniform": "Azul-marinho passando por cinza até amarelo, feito para parecer igual para quem tem daltonismo vermelho–verde e para qualquer outra pessoa. Perceptualmente uniforme", + "Black through deep blue and teal to mint. Perceptually uniform and colour-blind safe": "Preto passando por azul profundo e azul-petróleo até verde-menta. Perceptualmente uniforme e seguro para daltônicos", + "Black through purple and red to cream. Perceptually uniform and colour-blind safe": "Preto passando por roxo e vermelho até creme. Perceptualmente uniforme e seguro para daltônicos", + "A rainbow like jet, dark blue through green to dark red, but smooth — without the bands jet shows. Not for red–green colour blindness": "Um arco-íris como o jet, do azul escuro ao vermelho escuro passando por verde, mas suave — sem as faixas que o jet mostra. Não indicado para daltonismo vermelho–verde", + "Across": "Através de", + "Flip": "Inverter", + "What the plane is laid across: a coordinate plane through the part’s middle, or an element with a direction — a plane’s normal, the axis of a cylinder, cone or line, a circle’s normal. The half toward you is taken away.": "Sobre o que o plano é posicionado: um plano de coordenadas pelo meio da peça, ou um elemento com direção — a normal de um plano, o eixo de um cilindro, cone ou linha, a normal de um círculo. A metade voltada para você é removida.", + "Where along its direction the plane lies — drag the arrow on the part, or type it here": "Onde, ao longo da direção, o plano fica — arraste a seta na peça ou digite aqui", + "Take away the other half instead": "Remover a outra metade", + "set otherwise in its menu": "definido de outra forma no menu dele" +} diff --git a/desktop/main.js b/desktop/main.js new file mode 100644 index 00000000..1831e1b8 --- /dev/null +++ b/desktop/main.js @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// ScanRuler Desktop: serves the built ScanRuler and the agent link from this +// process and shows the app in its own window, paired with the link — no +// browser, no hosted site. The ScanRuler code is not touched. +import { app, BrowserWindow, Menu, dialog, ipcMain, session } from 'electron' +import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs' +import { basename, dirname, join, resolve } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { cpus } from 'node:os' +import { BACKENDS, gpuSwitches, loadSettings, saveSettings } from './settings.js' +import { LANGUAGES, injectScript, loadDict } from './i18n.js' +import { meshOfProject, watchMeshes } from './creality.js' + +// Native menu text; the page's own text goes through i18n.js. +const MENU = { + 'pt-BR': { file: 'Arquivo', open: 'Abrir scan…', quit: 'Sair', view: 'Exibir', help: 'Ajuda', hardware: 'Hardware…', language: 'Idioma', watch: 'Observar pasta do Creality Scan…', unwatch: 'Parar de observar o Creality Scan', fresh: 'Novo Mesh.ply do projeto', openIt: 'Abrir no ScanRuler', ignore: 'Ignorar' }, + en: { file: 'File', open: 'Open scan…', quit: 'Quit', view: 'View', help: 'Help', hardware: 'Hardware…', language: 'Language', watch: 'Watch Creality Scan folder…', unwatch: 'Stop watching Creality Scan', fresh: 'New Mesh.ply from project', openIt: 'Open in ScanRuler', ignore: 'Ignore' }, +} + +const here = dirname(fileURLToPath(import.meta.url)) +const DEFAULT_PORT = 7317 +const OPENABLE = /\.(scanruler|stl|ply|obj|3mf|step|stp|obp)$/i +const CHUNK = 12 * 1024 * 1024 // bytes of file per script, ~16 MB of base64 + +// Packaged: resources/app-dist and resources/mcp; from a checkout: ../src. +const distDir = app.isPackaged ? join(process.resourcesPath, 'app-dist') : resolve(here, '../dist') +const mcpDir = app.isPackaged ? join(process.resourcesPath, 'mcp') : resolve(here, '../mcp') + +// Graphics switches have to be set before Electron is ready. +const settings = loadSettings(app.getPath('userData')) +const backendRunning = settings.backend +for (const [name, value] of gpuSwitches(settings.backend)) app.commandLine.appendSwitch(name, value) + +// Nothing here talks to the network on its own. +process.env.SCANRULER_MCP_NO_UPDATE = '1' + +// A second copy hands its file to the first and leaves before starting anything. +const first = app.requestSingleInstanceLock() +if (!first) app.exit(0) + +let main = null +let link = null +let http = null +let pairingUrl = null +let angleActive = null +const pendingFiles = [] +let stopWatching = null +let asking = false + +const fileArg = (argv) => argv.slice(1).find((a) => OPENABLE.test(a) && existsSync(a)) + +/** Hand a file to the page as if it were dropped on it, which is the app's own way in. */ +async function openFile(path) { + if (/\.obp$/i.test(path)) path = meshOfProject(path) + if (!main || main.webContents.isLoading()) { + pendingFiles.push(path) + return + } + // Sent in pieces: a scan can be hundreds of MB, too much for one script string. + const bytes = readFileSync(path) + const wc = main.webContents + await wc.executeJavaScript('window.__srOpen = []') + for (let at = 0; at < bytes.length; at += CHUNK) { + await wc.executeJavaScript(`window.__srOpen.push("${bytes.subarray(at, at + CHUNK).toString('base64')}")`) + } + await wc.executeJavaScript(`(() => { + const parts = window.__srOpen.map((b64) => Uint8Array.from(atob(b64), (c) => c.charCodeAt(0))) + window.__srOpen = null + const dt = new DataTransfer() + dt.items.add(new File(parts, ${JSON.stringify(basename(path))})) + window.dispatchEvent(new DragEvent('drop', { dataTransfer: dt, bubbles: true, cancelable: true })) + })()`) +} + +/** Watch a Creality Scan projects folder; a finished Mesh.ply is offered to the page, never opened unasked. */ +function watchCreality(dir) { + stopWatching?.() + stopWatching = null + settings.crealityDir = dir + saveSettings(app.getPath('userData'), settings) + if (dir) { + try { + stopWatching = watchMeshes(dir, async (path, project) => { + if (asking) return + asking = true + try { + const m = MENU[settings.language] + const r = await dialog.showMessageBox(main ?? undefined, { type: 'question', message: `${m.fresh} “${project}”`, detail: path, buttons: [m.openIt, m.ignore], defaultId: 0, cancelId: 1 }) + if (r.response === 0) await openFile(path) + } catch (e) { + dialog.showErrorBox('ScanRuler', String(e)) + } finally { + asking = false + } + }) + } catch (e) { + dialog.showErrorBox('ScanRuler', String(e)) + settings.crealityDir = '' + saveSettings(app.getPath('userData'), settings) + } + } + buildMenu() +} + +// The page rejects a project.obp; a drop or a file pick of one goes to the main process instead, +// which opens the project's mesh (the page cannot see the folder around the file). +const PROJECT_HOOK = `(() => { + if (window.__srProjectHook) return + window.__srProjectHook = true + const isObp = (f) => f && /\\.obp$/i.test(f.name) + const take = (e, file) => { + e.preventDefault() + e.stopImmediatePropagation() + window.srProject.open(file) + } + window.addEventListener('drop', (e) => { const f = e.dataTransfer?.files?.[0]; if (isObp(f)) take(e, f) }, true) + document.addEventListener('change', (e) => { + const f = e.target?.type === 'file' && e.target.files?.[0] + if (isObp(f)) { take(e, f); e.target.value = '' } + }, true) +})()` + +async function startServer() { + const { httpServer } = await import(pathToFileURL(join(mcpDir, 'src/serve.js')).href) + const { SharedLink } = await import(pathToFileURL(join(mcpDir, 'src/relay.js')).href) + const { originAllowed } = await import(pathToFileURL(join(mcpDir, 'src/link.js')).href) + const { loadConfig } = await import(pathToFileURL(join(mcpDir, 'src/config.js')).href) + const version = JSON.parse(readFileSync(join(mcpDir, 'package.json'), 'utf8')).version + const port = Number(process.env.SCANRULER_MCP_PORT ?? DEFAULT_PORT) + const token = process.env.SCANRULER_MCP_TOKEN || loadConfig().token + http = httpServer({ serve: distDir, allowOrigin: (o) => originAllowed(o, []), version }) + link = new SharedLink({ http, port, token, version, log: (l) => console.log(`[mcp] ${l}`) }) + const role = new Promise((ok) => link.on('role', ok)) + link.start() + if ((await Promise.race([role, new Promise((r) => setTimeout(() => r('timeout'), 4000))])) !== 'holder') { + throw new Error( + `A porta ${port} já está em uso por outro scanruler-mcp ou programa. Feche-o ou defina SCANRULER_MCP_PORT.`, + ) + } + pairingUrl = `http://127.0.0.1:${port}/#agent=${port}:${token}` + // The agent entry (scanruler-mcp.cmd) reads this to join the app that is open. + writeFileSync(agentFile(), JSON.stringify({ port, token }), { mode: 0o600 }) +} + +const agentFile = () => join(app.getPath('userData'), 'agent.json') + +// Cross-origin isolation (SharedArrayBuffer, multithreaded WASM) for the local app only. +function isolate() { + session.defaultSession.webRequest.onHeadersReceived({ urls: ['http://127.0.0.1/*'] }, (d, cb) => { + cb({ + responseHeaders: { + ...d.responseHeaders, + 'Cross-Origin-Opener-Policy': ['same-origin'], + 'Cross-Origin-Embedder-Policy': ['require-corp'], + }, + }) + }) +} + +/** Put the page in the chosen language (English leaves it as it is). */ +function translate() { + main?.webContents.executeJavaScript(injectScript(loadDict(settings.language))).catch((e) => console.error('i18n', e)) +} + +function setLanguage(language) { + settings.language = language + saveSettings(app.getPath('userData'), settings) + translate() + buildMenu() +} + +function buildMenu() { + const m = MENU[settings.language] + Menu.setApplicationMenu( + Menu.buildFromTemplate([ + { + label: m.file, + submenu: [ + { + label: m.open, + accelerator: 'CmdOrCtrl+O', + click: async () => { + const r = await dialog.showOpenDialog(main, { filters: [{ name: 'Scan', extensions: ['stl', 'ply', 'obj', '3mf', 'step', 'stp', 'scanruler', 'obp'] }] }) + if (!r.canceled) openFile(r.filePaths[0]).catch((e) => dialog.showErrorBox('ScanRuler', String(e))) + }, + }, + { type: 'separator' }, + settings.crealityDir + ? { label: `${m.unwatch} (${basename(settings.crealityDir)})`, click: () => watchCreality('') } + : { + label: m.watch, + click: async () => { + const r = await dialog.showOpenDialog(main, { properties: ['openDirectory'] }) + if (!r.canceled) watchCreality(r.filePaths[0]) + }, + }, + { type: 'separator' }, + { role: 'quit', label: m.quit }, + ], + }, + { label: m.view, submenu: [{ role: 'reload' }, { role: 'togglefullscreen' }, { role: 'toggleDevTools' }] }, + { + label: m.language, + submenu: Object.entries(LANGUAGES).map(([id, label]) => ({ label, type: 'radio', checked: settings.language === id, click: () => setLanguage(id) })), + }, + { label: m.help, submenu: [{ label: m.hardware, click: showHardware }] }, + ]), + ) +} + +function createWindow() { + main = new BrowserWindow({ + width: 1500, + height: 950, + backgroundColor: '#15171a', + title: 'ScanRuler', + webPreferences: { preload: join(here, 'preload-main.cjs'), contextIsolation: true, sandbox: true, nodeIntegration: false }, + }) + main.webContents.setWindowOpenHandler(() => ({ action: 'deny' })) + main.webContents.on('will-navigate', (e, url) => { + if (!url.startsWith('http://127.0.0.1:')) e.preventDefault() + }) + main.webContents.on('did-finish-load', async () => { + translate() + main.webContents.executeJavaScript(PROJECT_HOOK).catch((e) => console.error('project hook', e)) + // The page's drop handler exists only once React has drawn; a file handed over earlier is lost. + for (let i = 0; i < 100 && main; i++) { + if (await main.webContents.executeJavaScript(`!!document.querySelector('#root *')`)) break + await new Promise((r) => setTimeout(r, 100)) + } + for (const f of pendingFiles.splice(0)) openFile(f).catch((e) => dialog.showErrorBox('ScanRuler', String(e.message ?? e))) + }) + main.on('closed', () => (main = null)) + main.loadURL(pairingUrl) +} + +async function pageProbe() { + if (!main) return {} + return main.webContents.executeJavaScript(`(async () => { + const out = { threads: navigator.hardwareConcurrency, isolated: crossOriginIsolated, webnn: !!navigator.ml, webgpu: 'indisponível' } + try { + const a = navigator.gpu && (await navigator.gpu.requestAdapter({ powerPreference: 'high-performance' })) + if (a) { const i = a.info ?? {}; out.webgpu = [i.vendor, i.architecture, i.description].filter(Boolean).join(' ') || 'disponível' } + } catch {} + try { + const gl = new OffscreenCanvas(1, 1).getContext('webgl2') + const x = gl && gl.getExtension('WEBGL_debug_renderer_info') + out.renderer = gl ? (x ? gl.getParameter(x.UNMASKED_RENDERER_WEBGL) : 'WebGL2') : 'sem WebGL2' + } catch {} + return out + })()`) +} + +function showHardware() { + const w = new BrowserWindow({ + width: 760, + height: 520, + title: 'ScanRuler — Hardware', + parent: main ?? undefined, + webPreferences: { preload: join(here, 'preload.cjs'), contextIsolation: true, sandbox: true }, + }) + w.removeMenu() + w.loadFile(join(here, 'hardware.html')) +} + +ipcMain.handle('project:open', (e, path) => { + if (e.sender !== main?.webContents || !/\.obp$/i.test(String(path))) return + openFile(path).catch((err) => dialog.showErrorBox('ScanRuler', String(err.message ?? err))) +}) +ipcMain.handle('hw:info', async () => { + const gpu = await app.getGPUInfo('complete').catch(() => ({})) + const gpus = (gpu.gpuDevice ?? []).map((d) => `${d.deviceString ?? '?'} (vendor 0x${(d.vendorId ?? 0).toString(16)})`) + angleActive = gpu.auxAttributes?.glImplementationParts ?? gpu.auxAttributes?.glRenderer ?? angleActive + return { + gpus, + backend: settings.backend, + backendRunning, + backendLabel: BACKENDS[backendRunning].label, + backends: Object.fromEntries(Object.entries(BACKENDS).map(([k, v]) => [k, v.label])), + angleActive, + cpu: cpus()[0]?.model.trim() ?? '?', + page: await pageProbe(), + versions: `Electron ${process.versions.electron} / Chromium ${process.versions.chrome}`, + } +}) +ipcMain.handle('hw:set-backend', (_e, backend) => { + if (backend in BACKENDS) saveSettings(app.getPath('userData'), { ...settings, backend: (settings.backend = backend) }) +}) +ipcMain.handle('hw:restart', () => { + app.relaunch() + app.quit() +}) + +app.on('second-instance', (_e, argv) => { + if (main) { + if (main.isMinimized()) main.restore() + main.focus() + } + const f = fileArg(argv) + if (f) openFile(f).catch((e) => console.error(e)) +}) + +app.whenReady().then(async () => { + if (!first) return + try { + await startServer() + } catch (e) { + dialog.showErrorBox('ScanRuler', String(e.message ?? e)) + app.quit() + return + } + isolate() + buildMenu() + createWindow() + if (settings.crealityDir && existsSync(settings.crealityDir)) watchCreality(settings.crealityDir) + const f = fileArg(process.argv) + if (f) pendingFiles.push(f) +}) + +app.on('window-all-closed', () => app.quit()) +app.on('before-quit', () => { + link?.close() + try { + unlinkSync(agentFile()) + } catch {} +}) diff --git a/desktop/package-lock.json b/desktop/package-lock.json new file mode 100644 index 00000000..a956d0df --- /dev/null +++ b/desktop/package-lock.json @@ -0,0 +1,3899 @@ +{ + "name": "scanruler-desktop", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "scanruler-desktop", + "version": "0.1.0", + "license": "AGPL-3.0-only", + "devDependencies": { + "electron": "^45.2.1", + "electron-builder": "^26.18.0", + "puppeteer-core": "^25.16.0" + } + }, + "node_modules/@electron-internal/extract-zip": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.8.tgz", + "integrity": "sha512-8iajeLFbzNwM0m3AsQZSZgYREWglAImyGvvFSWU34FgMW8sHlzdwj9QKvv9dgN7jMP591EFUF/sLOC3Jl9YIjQ==", + "dev": true, + "license": "MIT", + "engines": { + "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.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "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.1.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.1.0.tgz", + "integrity": "sha512-3kSBtG8ObcTVfXanm5vVJ6UnBLEVmVsRk1M+vGqCuMBV+XLCbJYuWQful+yIy0GQDsSlK0kHEriEHn7SPk4EnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^3.0.0", + "graceful-fs": "^4.2.11", + "progress": "^2.0.3", + "semver": "^7.6.3", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=22.12.0" + }, + "optionalDependencies": { + "undici": "^7.24.4" + } + }, + "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.2.1", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.2.1.tgz", + "integrity": "sha512-IS4pG3BysRrCLJ8xTAfvg+IQDbiUoAzdJy2SfjcYKnLDy5DpCdjnPdhS5Zf5UMfrkB24xu0KC+TqcOxg+eG+6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.1.1", + "node-abi": "^4.2.0", + "node-api-version": "^0.2.1", + "node-gyp": "^12.2.0", + "read-binary-file-arch": "^1.0.6" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "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.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.1.tgz", + "integrity": "sha512-KYAb4c9BJQI6QqGKthV68OHe0badztdXJWKo0WtBA9IuCFPTKvE5ZdUBglP833aMjhaSPNO4A5j/EkzZtGlKjA==", + "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.4.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.1.tgz", + "integrity": "sha512-KYAb4c9BJQI6QqGKthV68OHe0badztdXJWKo0WtBA9IuCFPTKvE5ZdUBglP833aMjhaSPNO4A5j/EkzZtGlKjA==", + "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/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "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/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.11.1.tgz", + "integrity": "sha512-jaU9OaCcbZUdyXx9hMETZB9xoNmxTGjOGFqd3cOwKFkpKnyKd9WxF3uqj4PvX4cyOMP9bSdHg5YyML2qtx/d/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@peculiar/utils": "^2.0.2", + "asn1js": "^3.0.10", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.7.2.tgz", + "integrity": "sha512-ktTSACRJ5WdpfjHjFT2EWJQQPacGv1cPCDfEbdmpAHOxdFiYqPe4lfnXGA3D8Dg9/0kHPYSvyRlgujIOVlTdvQ==", + "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/@puppeteer/browsers": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.2.6.tgz", + "integrity": "sha512-MJP29gVoHznmqmwhH1tlZqzfeQ3aylTpSSa6WKZXtkwYUH02i9imBmWGTyZrbnAou5paX78SE4jMAUqnEPf8ug==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "modern-tar": "^0.8.6", + "yargs": "^18.0.0" + }, + "bin": { + "browsers": "lib/main-cli.js" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "proxy-agent": ">=8.0.1", + "yauzl": "^2.10.0 || ^3.4.0" + }, + "peerDependenciesMeta": { + "proxy-agent": { + "optional": true + }, + "yauzl": { + "optional": true + } + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-regex": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz", + "integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@puppeteer/browsers/node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@puppeteer/browsers/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@puppeteer/browsers/node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@puppeteer/browsers/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "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/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/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": "24.19.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.2.tgz", + "integrity": "sha512-93d49wCWJkCyN87MxLpNN6KXPCwFw7bVUg79mGUV1OS40JGWwVCr8n61DXCSqz7CV6/VfPkb7xec7uP2M+QHWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "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/@xmldom/xmldom": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.15.tgz", + "integrity": "sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/abbrev": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "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", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "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": "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.18.0", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.18.0.tgz", + "integrity": "sha512-lx+KkuCSZHcbECf4R3IAcC9VOFk0+cMUwljO63s5y8yqS1Lw0sRGmjPaWsPwP3zv3ohZYbMhUvjXeiQXXZhJXA==", + "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.2.1", + "@electron/universal": "2.0.3", + "@malept/flatpak-bundler": "^0.4.0", + "@noble/hashes": "^1.8.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.16.0", + "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.18.0", + "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.18.0", + "electron-builder-squirrel-windows": "26.18.0" + } + }, + "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/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/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.1.0", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.1.0.tgz", + "integrity": "sha512-HWYi1THkOqguCiO+vwj09ZkQFD2kKEsejYHAYCtX0D80WVdoDnvRc9IukXX/jeb0T9NRWJnRtqwdbHltnFjwXA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.6", + "pvutils": "^1.2.0", + "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/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/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.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "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.16.0", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.16.0.tgz", + "integrity": "sha512-RLyJhB7Si3YkzKR9ubQslWuXW3Vhs3CGe1i+SeixBZ0qTd1mk3XBmssvY22TlB6CS5blyko8Gu1JzpYk8UkYAg==", + "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/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/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", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chromium-bidi": { + "version": "157.0.8090-0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-157.0.8090-0.tgz", + "integrity": "sha512-KJw7IQgqXwxpJgJlEIPg5mr2ZoqA+jkTRtc5EABI7sUJ3bu3cpI7NuEBoj4Gn1AsF78CaT9So5WQWspK3/wYag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^4.6.5" + }, + "engines": { + "node": ">=20.19.0 <22.0.0 || >=22.12.0" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "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/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/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "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.4.0" + } + }, + "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", + "optional": true + }, + "node_modules/devtools-protocol": { + "version": "0.0.1704951", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1704951.tgz", + "integrity": "sha512-GQhQOs9u/Mvm1zMtJIH872JS4AskuZdUSsPNPk4ChahtWAUqgz6atEtIsMt+nO7MqF1Ic4HxII/k8Cu36JivNg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "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": { + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " + } + }, + "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.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "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": "*" + } + }, + "node_modules/dmg-builder": { + "version": "26.18.0", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.18.0.tgz", + "integrity": "sha512-ozCGC54wi2C1RkS2v69y9lQxvoQ1Coh+iT8CmWiyY8y1ZuOKVvuNson2Hd+HEOs9+EZNNsOBbzKgQZ4SNqWd0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.18.0", + "builder-util": "26.16.0", + "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": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "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": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "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/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": "45.2.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-45.2.1.tgz", + "integrity": "sha512-t433yuo8Kh8WG/mTStnPRT/NtpWw8A+caNLJgeU0tS/FlfOkeNEntFsac4ViLgDxZOmDe7BeSkOANVqrqNHhDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron-internal/extract-zip": "^1.0.1", + "@electron/get": "^5.0.0", + "@types/node": "^24.9.0" + }, + "bin": { + "electron": "cli.js", + "install-electron": "install.js" + }, + "engines": { + "node": ">= 22.12.0" + } + }, + "node_modules/electron-builder": { + "version": "26.18.0", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.18.0.tgz", + "integrity": "sha512-ASgIuQnOq4RuD8jMhS9hI0LS3EMRT1I/M5R6utqifB/BHN80IcCjBdt+DED+NmNPvmBgEtrlExXZitNdA7kXOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "26.18.0", + "builder-util": "26.16.0", + "builder-util-runtime": "9.7.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "dmg-builder": "26.18.0", + "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.18.0", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.18.0.tgz", + "integrity": "sha512-a2elgTipboXNIsxqS1HOmHMGp4Pd3LJgIZOdIFTOskBSawvxKyZWYWQ1d7KKA4CtINSchYCYEKzhxRqiKZev9Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "26.18.0", + "builder-util": "26.16.0", + "electron-winstaller": "5.4.0" + } + }, + "node_modules/electron-publish": { + "version": "26.18.0", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.18.0.tgz", + "integrity": "sha512-MagnqaZSALSWjcdKu5HEVhYUZD/2BDL3q7XzrqWwub6gf/UfD3ItMgmthVDv9w5y9E0gH1C3JWqKaXwf6f4ROg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "aws4": "^1.13.2", + "builder-util": "26.16.0", + "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-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/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/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": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "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/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz", + "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "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.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "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/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/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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "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.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "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/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", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "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", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "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", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "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", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/http-cache-semantics": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.3.0.tgz", + "integrity": "sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "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", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "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/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": "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/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/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "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/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", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "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", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "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/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": "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/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", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "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.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/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/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.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "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", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "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/modern-tar": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.8.6.tgz", + "integrity": "sha512-LYSks0tNVqbtez/9OB0YFP6qLRSqkdAxJWOQpVaR67lNTbj2zNLyYpowkRMia5lXLCZKzxwoxkxH2MbbBR2aEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.40.0.tgz", + "integrity": "sha512-GuvqvtrOk+AC27RidBxBsMzVNkwNl7PjHgc4NXbmdvSVLgIUjpaqcG1FjNRYnt2Yn9Ytq+Ln37XsBaglUcte/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.6.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/node-api-version": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", + "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/node-gyp": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/node-gyp/node_modules/undici": { + "version": "6.29.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.1.tgz", + "integrity": "sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "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/nopt": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^4.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "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-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/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "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/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", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkijs": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.3.tgz", + "integrity": "sha512-V9kOzaItwDgqlryhXysQsAXUEKi5O7kqWUaP3dXI7MLvkPqWWMb4wVb1QAtmEDCKI+XIAINsZoNKMFF7sZb1eg==", + "dev": true, + "license": "BSD-3-Clause", + "workspaces": [ + "website" + ], + "dependencies": { + "@noble/hashes": "1.8.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/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/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", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "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", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "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/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/puppeteer-core": { + "version": "25.16.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.16.0.tgz", + "integrity": "sha512-VA5Pf54N8shfopNkZZXCdiiwfnnY8ZJYAMqFQRhlH5mGUhfwf53Ar770GPtdpTDLUqvQeYpIBtC9/G2Jij2IJw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "3.2.6", + "chromium-bidi": "157.0.8090-0", + "devtools-protocol": "0.0.1704951", + "typed-query-selector": "^2.12.3", + "webdriver-bidi-protocol": "1.0.1", + "ws": "^8.22.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "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.2.0", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.2.0.tgz", + "integrity": "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "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/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==", + "dev": true, + "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": ">= 4" + } + }, + "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": "ISC", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "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": ">=8.0" + } + }, + "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/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.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "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/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/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/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/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-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/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", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "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.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "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", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "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/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.9.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.9.1.tgz", + "integrity": "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==", + "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/typed-query-selector": { + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.3.tgz", + "integrity": "sha512-/dtuMZyutHLlrxWhhC1uib00ELUr2dZ8C9B8YzZYsJmeE3V56OxRblrSM2UaF142pcMtak7ehZ6+poWGeehNIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.1.tgz", + "integrity": "sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "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/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/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/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/webdriver-bidi-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-1.0.1.tgz", + "integrity": "sha512-WXWs/aXF2a6Al1Z2/V/7Gp2oPZOAAmHjJOTmv4KCXRJ122IMj3tSSMc6a4q3K4QOmhQbse/Ahr+oTVJOU2dUiw==", + "dev": true, + "license": "Apache-2.0" + }, + "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/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", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.22.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.1.tgz", + "integrity": "sha512-oyL6OPYme/CmiFvNuGzeoxCJ4nOrZE2O1FwM9MWM1AsGri6nLXB2pg+lNA8aLeXMSav0J6eIVS1j6vUPzUtK9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "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": "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/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.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/desktop/package.json b/desktop/package.json new file mode 100644 index 00000000..8d2581d7 --- /dev/null +++ b/desktop/package.json @@ -0,0 +1,81 @@ +{ + "name": "scanruler-desktop", + "version": "0.1.0", + "description": "ScanRuler desktop shell (Electron)", + "main": "main.js", + "type": "module", + "license": "AGPL-3.0-only", + "scripts": { + "start": "electron .", + "test": "node test/smoke.mjs && node test/creality.mjs", + "dist": "electron-builder --win dir" + }, + "build": { + "appId": "local.scanruler.desktop", + "productName": "ScanRuler", + "directories": { + "output": "release" + }, + "files": [ + "main.js", + "preload.cjs", + "preload-main.cjs", + "settings.js", + "creality.js", + "i18n.js", + "i18n/*.json", + "hardware.html", + "package.json" + ], + "extraResources": [ + { "from": "scanruler-mcp.cmd", "to": "../scanruler-mcp.cmd" }, + { "from": "agent-bridge.mjs", "to": "agent-bridge.mjs" }, + { + "from": "../dist", + "to": "app-dist" + }, + { + "from": "../mcp", + "to": "mcp", + "filter": [ + "**/*", + "!tests/**" + ] + }, + { + "from": "../mcp/node_modules", + "to": "mcp/node_modules" + } + ], + "win": { + "target": "dir" + }, + "fileAssociations": [ + { + "ext": "scanruler", + "name": "ScanRuler project" + }, + { + "ext": "stl", + "name": "STL mesh" + }, + { + "ext": "ply", + "name": "PLY mesh" + }, + { + "ext": "obj", + "name": "OBJ mesh" + }, + { + "ext": "3mf", + "name": "3MF mesh" + } + ] + }, + "devDependencies": { + "electron": "^45.2.1", + "electron-builder": "^26.18.0", + "puppeteer-core": "^25.16.0" + } +} diff --git a/desktop/preload-main.cjs b/desktop/preload-main.cjs new file mode 100644 index 00000000..cd0c5640 --- /dev/null +++ b/desktop/preload-main.cjs @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Preload of the main window: one door, for a Creality project.obp the user drops or picks. +// The page gets no file paths and no Node; the main process only opens the .obp it is handed. +const { contextBridge, ipcRenderer, webUtils } = require('electron') + +contextBridge.exposeInMainWorld('srProject', { + open: (file) => ipcRenderer.invoke('project:open', webUtils.getPathForFile(file)), +}) diff --git a/desktop/preload.cjs b/desktop/preload.cjs new file mode 100644 index 00000000..8997db4c --- /dev/null +++ b/desktop/preload.cjs @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Preload of the Hardware panel only: the main window gets none, so the +// ScanRuler page has no path to Node. +const { contextBridge, ipcRenderer } = require('electron') + +contextBridge.exposeInMainWorld('hw', { + info: () => ipcRenderer.invoke('hw:info'), + setBackend: (backend) => ipcRenderer.invoke('hw:set-backend', backend), + restart: () => ipcRenderer.invoke('hw:restart'), +}) diff --git a/desktop/scanruler-mcp.cmd b/desktop/scanruler-mcp.cmd new file mode 100644 index 00000000..921cd981 --- /dev/null +++ b/desktop/scanruler-mcp.cmd @@ -0,0 +1,5 @@ +@echo off +rem Agent entry point: claude mcp add scanruler -- "\scanruler-mcp.cmd" +set ELECTRON_RUN_AS_NODE=1 +set SCANRULER_MCP_NO_UPDATE=1 +"%~dp0ScanRuler.exe" "%~dp0resources\agent-bridge.mjs" %* diff --git a/desktop/settings.js b/desktop/settings.js new file mode 100644 index 00000000..192e008e --- /dev/null +++ b/desktop/settings.js @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Hardware choices kept in hardware.json under userData, read before Electron +// is ready because the graphics switches must be set before then. +import { readFileSync, writeFileSync, mkdirSync } from 'node:fs' +import { dirname, join } from 'node:path' + +/** The graphics backend ANGLE translates WebGL to, by the name the panel shows. */ +export const BACKENDS = { + d3d11: { label: 'DirectX 11 (default)', angle: 'd3d11' }, + d3d12: { label: 'DirectX 12 (experimental)', angle: 'd3d11on12' }, + vulkan: { label: 'Vulkan (experimental)', angle: 'vulkan' }, + gl: { label: 'OpenGL (experimental)', angle: 'gl' }, +} + +const LANGS = ['pt-BR', 'en'] +// The OS language picks the first language; the menu changes it. +const DEFAULTS = { backend: 'd3d11', language: Intl.DateTimeFormat().resolvedOptions().locale.startsWith('pt') ? 'pt-BR' : 'en', crealityDir: '' } + +export function loadSettings(dir) { + try { + const saved = JSON.parse(readFileSync(join(dir, 'hardware.json'), 'utf8')) + return { + ...DEFAULTS, + ...(saved.backend in BACKENDS ? { backend: saved.backend } : {}), + ...(LANGS.includes(saved.language) ? { language: saved.language } : {}), + ...(typeof saved.crealityDir === 'string' ? { crealityDir: saved.crealityDir } : {}), + } + } catch { + return { ...DEFAULTS } + } +} + +export function saveSettings(dir, settings) { + const file = join(dir, 'hardware.json') + mkdirSync(dirname(file), { recursive: true }) + writeFileSync(file, JSON.stringify(settings, null, 2)) +} + +/** The Chromium switches for a backend: [name, value?] pairs. */ +export function gpuSwitches(backend) { + const features = ['WebGPU'] + if (backend === 'vulkan') features.push('Vulkan') + return [ + ['use-angle', BACKENDS[backend].angle], + ['enable-features', features.join(',')], + ['enable-unsafe-webgpu'], + ['ignore-gpu-blocklist'], + ['enable-gpu-rasterization'], + ['enable-zero-copy'], + ] +} diff --git a/desktop/test/creality.mjs b/desktop/test/creality.mjs new file mode 100644 index 00000000..c1fb1abd --- /dev/null +++ b/desktop/test/creality.mjs @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// The watcher reports a Mesh.ply once, after it stops growing, and ignores the rest of a project. +import { mkdtempSync, mkdirSync, writeFileSync, appendFileSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import assert from 'node:assert/strict' +import { isMesh, meshOfProject, watchMeshes } from '../creality.js' +import { utimesSync } from 'node:fs' + +assert(isMesh('C:\\Scan\\P\\data\\result\\Mesh.ply')) +assert(isMesh('/scan/p/data/result/mesh.ply')) +assert(!isMesh('C:\\Scan\\P\\data\\result\\Fused.ply')) +assert(!isMesh('C:\\Scan\\P\\Mesh.ply')) + +// project.obp: the final mesh, else the newest session's fused scan, else a clear error +const proj = mkdtempSync(join(tmpdir(), 'obp-')) +const put = (...p) => { mkdirSync(join(proj, ...p.slice(0, -1)), { recursive: true }); writeFileSync(join(proj, ...p), 'x') } +writeFileSync(join(proj, 'project.obp'), 'name: project\ndata_dir: dData\ncombinations:\n []\nsessions:\n - sOld\n - sNew\nplatform: windows\n') +assert.throws(() => meshOfProject(join(proj, 'project.obp')), /ainda não tem malha/) +put('sOld', 'result', 'Fused.ply'); put('sNew', 'result', 'Fused.ply') +utimesSync(join(proj, 'sOld', 'result', 'Fused.ply'), new Date(2020, 0, 1), new Date(2020, 0, 1)) +assert.equal(meshOfProject(join(proj, 'project.obp')), join(proj, 'sNew', 'result', 'Fused.ply')) +put('dData', 'result', 'Mesh.ply') +assert.equal(meshOfProject(join(proj, 'project.obp')), join(proj, 'dData', 'result', 'Mesh.ply')) +rmSync(proj, { recursive: true, force: true }) + +const dir = mkdtempSync(join(tmpdir(), 'creality-')) +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)) +const got = [] +const stop = watchMeshes(dir, (path, project) => got.push({ path, project }), { settleMs: 150 }) +try { + const result = join(dir, 'Peca_1', '20260101', 'result') + mkdirSync(result, { recursive: true }) + writeFileSync(join(result, 'Fused.ply'), 'x') // not the final mesh + writeFileSync(join(result, 'Mesh.ply'), 'a') + await sleep(100) + appendFileSync(join(result, 'Mesh.ply'), 'b') // still being written + await sleep(100) + appendFileSync(join(result, 'Mesh.ply'), 'c') + await sleep(800) + assert.equal(got.length, 1, `one report after it settled, got ${got.length}`) + assert.equal(got[0].project, 'Peca_1') + assert(got[0].path.endsWith('Mesh.ply')) + appendFileSync(join(result, 'Mesh.ply'), 'd') // a re-mesh later reports again + await sleep(800) + assert.equal(got.length, 2) + console.log('creality watcher ok') +} finally { + stop() + rmSync(dir, { recursive: true, force: true }) +} diff --git a/desktop/test/e2e-agent.mjs b/desktop/test/e2e-agent.mjs new file mode 100644 index 00000000..4dc1676e --- /dev/null +++ b/desktop/test/e2e-agent.mjs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// End to end: the app pairs with the agent link; an MCP client reaches the page through the relay. +import { spawn } from 'node:child_process' +import { createRequire } from 'node:module' +import { mkdtempSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import puppeteer from 'puppeteer-core' +import assert from 'node:assert/strict' + +const electron = createRequire(import.meta.url)('electron') +// Its own user data: the single-instance lock is per profile, so an installed app stays untouched. +const userData = `--user-data-dir=${mkdtempSync(join(tmpdir(), 'scanruler-test-'))}` +const app = spawn(electron, ['.', '--remote-debugging-port=9334', userData], { cwd: new URL('..', import.meta.url), env: { ...process.env, SCANRULER_MCP_PORT: '7398' }, stdio: 'ignore' }) +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)) +let mcp +try { + let browser + for (let i = 0; i < 60 && !browser; i++) { browser = await puppeteer.connect({ browserURL: 'http://127.0.0.1:9334' }).catch(() => null); if (!browser) await sleep(500) } + let page + for (let i = 0; i < 60 && !page; i++) { page = (await browser.pages()).find((p) => p.url().startsWith('http://127.0.0.1:7398/')); if (!page) await sleep(500) } + await page.waitForSelector('#root *', { timeout: 30000 }) + await sleep(1500) + const buttons = await page.$$eval('button', (b) => b.map((x) => x.textContent.trim()).filter(Boolean)) + console.log('buttons:', buttons.slice(0, 12)) + const accept = await page.evaluateHandle(() => [...document.querySelectorAll('button')].find((b) => /accept|connect|conectar|aceit|understand/i.test(b.textContent))) + if (accept.asElement()) await accept.asElement().click() + await sleep(2000) + mcp = spawn(process.execPath, ['../mcp/bin/scanruler-mcp.js', '--port', '7398', '--no-update'], { stdio: ['pipe', 'pipe', 'inherit'] }) + const send = (o) => mcp.stdin.write(JSON.stringify(o) + '\n') + let buf = '' + const replies = new Map() + mcp.stdout.on('data', (d) => { buf += d; let i; while ((i = buf.indexOf('\n')) >= 0) { const m = JSON.parse(buf.slice(0, i)); buf = buf.slice(i + 1); replies.set(m.id, m) } }) + const call = async (id, method, params) => { send({ jsonrpc: '2.0', id, method, params }); for (let i = 0; i < 60 && !replies.has(id); i++) await sleep(250); return replies.get(id) } + await call(1, 'initialize', { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'smoke', version: '0' } }) + send({ jsonrpc: '2.0', method: 'notifications/initialized' }) + const r = await call(2, 'tools/call', { name: 'scanruler_status', arguments: {} }) + const text = JSON.stringify(r) + console.log(text.slice(0, 600)) + assert(/connected|page/i.test(text)) +} finally { mcp?.kill(); app.kill() } diff --git a/desktop/test/i18n.mjs b/desktop/test/i18n.mjs new file mode 100644 index 00000000..445b9bf9 --- /dev/null +++ b/desktop/test/i18n.mjs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// The page is translated, stays translated as React redraws, and goes back to English. +import { spawn } from 'node:child_process' +import { createRequire } from 'node:module' +import { mkdtempSync, readFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import puppeteer from 'puppeteer-core' +import assert from 'node:assert/strict' + +const electron = createRequire(import.meta.url)('electron') +// Its own user data: the single-instance lock is per profile, so an installed app stays untouched. +const userData = `--user-data-dir=${mkdtempSync(join(tmpdir(), 'scanruler-test-'))}` +const dict = JSON.parse(readFileSync(new URL('../i18n/pt-BR.json', import.meta.url), 'utf8')) +const child = spawn(electron, ['.', '--remote-debugging-port=9337', userData], { + cwd: new URL('..', import.meta.url), + env: { ...process.env, SCANRULER_MCP_PORT: '7395' }, + stdio: 'ignore', +}) +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)) +const text = (page) => page.evaluate(() => document.body.textContent) +try { + let browser + for (let i = 0; i < 60 && !browser; i++) { browser = await puppeteer.connect({ browserURL: 'http://127.0.0.1:9337' }).catch(() => null); if (!browser) await sleep(500) } + let page + for (let i = 0; i < 60 && !page; i++) { page = (await browser.pages()).find((p) => p.url().startsWith('http://127.0.0.1:7395/')); if (!page) await sleep(500) } + await page.waitForSelector('#root *', { timeout: 30000 }); await sleep(2500) + let t = await text(page) + assert(t.includes(dict['Wall Thickness']), 'tab translated') + assert(!/\bWall Thickness\b/.test(t), 'no English tab left') + // a click makes React draw new text; it must arrive translated + await page.evaluate(() => [...document.querySelectorAll('button')].find((b) => /Espessura|Wall/i.test(b.textContent))?.click()) + await sleep(800) + t = await text(page) + assert(t.includes(dict['Measure wall thickness']), 'panel drawn after a click is translated') + // back to English restores everything + await page.evaluate(() => window.__srI18n.set(null)) + await sleep(300) + t = await text(page) + assert(t.includes('Measure wall thickness') && !t.includes(dict['Measure wall thickness']), 'English restored') + // Short or unchanged values ("Auto") also occur inside English words, so only count real translations. + const left = Object.entries(dict).filter(([k, v]) => v !== k && v.length > 12 && t.includes(v)) + assert.equal(left.length, 0, `Portuguese left after restore: ${left.map(([, v]) => v).join(' | ')}`) + console.log('i18n ok') + await browser.disconnect() +} finally { child.kill() } diff --git a/desktop/test/smoke.mjs b/desktop/test/smoke.mjs new file mode 100644 index 00000000..2cb5c80c --- /dev/null +++ b/desktop/test/smoke.mjs @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Starts the desktop app on a spare debugging port and checks what the page +// sees: cross-origin isolation, thread count, WebGL2 and WebGPU. +import { spawn } from 'node:child_process' +import { createRequire } from 'node:module' +import { mkdtempSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import puppeteer from 'puppeteer-core' +import assert from 'node:assert/strict' + +const electron = createRequire(import.meta.url)('electron') +// Its own user data: the single-instance lock is per profile, so an installed app stays untouched. +const userData = `--user-data-dir=${mkdtempSync(join(tmpdir(), 'scanruler-test-'))}` +const port = 9333 +const exe = process.env.APP_EXE +const child = spawn(exe ?? electron, [...(exe ? [] : ['.']), `--remote-debugging-port=${port}`, userData], { + cwd: new URL('..', import.meta.url), + env: { ...process.env, SCANRULER_MCP_PORT: '7399' }, + stdio: 'inherit', +}) +const stop = () => child.kill() +process.on('exit', stop) + +try { + let browser + for (let i = 0; i < 60 && !browser; i++) { + browser = await puppeteer.connect({ browserURL: `http://127.0.0.1:${port}` }).catch(() => null) + if (!browser) await new Promise((r) => setTimeout(r, 500)) + } + assert(browser, 'app did not expose the debugging port') + let page + for (let i = 0; i < 60 && !page; i++) { + page = (await browser.pages()).find((p) => p.url().startsWith('http://127.0.0.1:7399/')) + if (!page) await new Promise((r) => setTimeout(r, 500)) + } + assert(page, 'main window never loaded the local app') + await page.waitForSelector('#root *', { timeout: 30000 }) + const info = await page.evaluate(async () => ({ + isolated: crossOriginIsolated, + threads: navigator.hardwareConcurrency, + webgl2: !!new OffscreenCanvas(1, 1).getContext('webgl2'), + webgpu: !!(navigator.gpu && (await navigator.gpu.requestAdapter())), + })) + console.log(info) + assert.equal(info.isolated, true, 'crossOriginIsolated') + assert(info.threads >= 4, 'threads') + assert(info.webgl2, 'webgl2') + assert(info.webgpu, 'webgpu') + console.log('smoke ok') + await browser.disconnect() +} finally { + stop() +} From d2211b9321009eb3a3981f6686fd62db4df9f639 Mon Sep 17 00:00:00 2001 From: Israel Fernandes Date: Sun, 1 Nov 2026 14:32:55 -0300 Subject: [PATCH 2/4] Desktop shell: seven languages, menu and panel translated, 3D API in the title, relay leaves with the app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - i18n: a language is a file in i18n/ (de, es, fr, it, ja, pt-BR, zh-CN); each carries _name, _menu and _hw so the native menu and the Hardware panel follow the page. First language from the OS locale; English fallback. pt-BR reviewed (Extrudar, Contrair/expandir, manipulador…). test/dicts.mjs checks every dictionary against the English keys. - The window title names the graphics API in use (ScanRuler — Vulkan); the Hardware panel words its rows from the dictionary. - agent-bridge.mjs exits when the app's agent.json disappears, so a relay started for the app cannot take its port over after it quits; the app also closes its HTTP server on quit. Co-Authored-By: Claude Fable 5.1 --- desktop/README.md | 13 +- desktop/agent-bridge.mjs | 10 +- desktop/hardware.html | 33 +-- desktop/i18n.js | 46 +++- desktop/i18n/_english-keys.json | 347 +++++++++++++++++++++++++++++ desktop/i18n/de.json | 384 ++++++++++++++++++++++++++++++++ desktop/i18n/es.json | 384 ++++++++++++++++++++++++++++++++ desktop/i18n/fr.json | 384 ++++++++++++++++++++++++++++++++ desktop/i18n/it.json | 384 ++++++++++++++++++++++++++++++++ desktop/i18n/ja.json | 384 ++++++++++++++++++++++++++++++++ desktop/i18n/pt-BR.json | 61 ++++- desktop/i18n/zh-CN.json | 384 ++++++++++++++++++++++++++++++++ desktop/main.js | 33 ++- desktop/package.json | 2 +- desktop/settings.js | 17 +- desktop/test/dicts.mjs | 34 +++ 16 files changed, 2833 insertions(+), 67 deletions(-) create mode 100644 desktop/i18n/_english-keys.json create mode 100644 desktop/i18n/de.json create mode 100644 desktop/i18n/es.json create mode 100644 desktop/i18n/fr.json create mode 100644 desktop/i18n/it.json create mode 100644 desktop/i18n/ja.json create mode 100644 desktop/i18n/zh-CN.json create mode 100644 desktop/test/dicts.mjs diff --git a/desktop/README.md b/desktop/README.md index a13e23ea..e8c6758c 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -10,7 +10,7 @@ cd desktop npm install npm start # runs the app from the checkout npm run dist # packages release/win-unpacked/ScanRuler.exe -npm test # smoke (isolation, threads, WebGL2, WebGPU) and the Creality watcher +npm test # dictionaries, smoke (isolation, threads, WebGL2, WebGPU), Creality watcher node test/i18n.mjs # the page stays translated as React redraws node test/e2e-agent.mjs # an MCP client reaches the page through the relay ``` @@ -22,12 +22,15 @@ Windows x64 only for now (`electron-builder --win dir`). - **Graphics backend**: Help → Hardware picks the backend ANGLE translates WebGL to — DirectX 11 (default), DirectX 12, Vulkan or OpenGL — and shows the GPU, WebGPU, thread count, cross-origin isolation and WebNN. A change applies on - restart. + restart; the window title names the API in use. - **Language**: the ScanRuler page is translated at run time, node by node, from `i18n/.json`, and kept translated as React redraws; the source of the - app is not touched. Today: `pt-BR`. The first language follows the OS; the - Language menu changes it. Text drawn on canvas and numbers built at run time - are not covered. + app is not touched. Shipped: Deutsch, Español, Français, Italiano, 日本語, + Português (Brasil), 简体中文. The first language follows the OS; the Language + menu changes it; the shell's own menu and Hardware panel follow. To add one, + copy `i18n/_english-keys.json` to `i18n/.json` as `{key: translation}` + with `_name`, `_menu` and `_hw` as in the others — `node test/dicts.mjs` + checks it. Text drawn on canvas and numbers built at run time are not covered. - **Creality Scan projects**: `project.obp` opens from the menu, the command line, drag and drop or the file picker, resolving to the project's `result/Mesh.ply` (or the newest session's `Fused.ply`). File → Watch Creality diff --git a/desktop/agent-bridge.mjs b/desktop/agent-bridge.mjs index 5dba59ed..adffc826 100644 --- a/desktop/agent-bridge.mjs +++ b/desktop/agent-bridge.mjs @@ -2,14 +2,20 @@ // Entry of scanruler-mcp.cmd: joins the ScanRuler app that is open — its port and token are // kept in agent.json beside its settings — then runs the agent server. Without the app // open (no file), or with SCANRULER_MCP_TOKEN / _PORT set by hand, nothing changes. -import { readFileSync } from 'node:fs' +import { existsSync, readFileSync } from 'node:fs' import { dirname, join } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' const here = dirname(fileURLToPath(import.meta.url)) +const file = process.env.SCANRULER_AGENT_FILE ?? join(process.env.APPDATA ?? '', 'scanruler-desktop', 'agent.json') try { - const a = JSON.parse(readFileSync(process.env.SCANRULER_AGENT_FILE ?? join(process.env.APPDATA ?? '', 'scanruler-desktop', 'agent.json'), 'utf8')) + const a = JSON.parse(readFileSync(file, 'utf8')) process.env.SCANRULER_MCP_TOKEN ||= a.token process.env.SCANRULER_MCP_PORT ||= String(a.port) + // The app removes the file when it quits. Left running, this server would take the + // app's port over and the next app could not start; so it leaves with the app. + setInterval(() => { + if (!existsSync(file)) process.exit(0) + }, 2000) } catch {} await import(pathToFileURL(join(here, 'mcp/bin/scanruler-mcp.js')).href) diff --git a/desktop/hardware.html b/desktop/hardware.html index 676df497..6ae4d2bf 100644 --- a/desktop/hardware.html +++ b/desktop/hardware.html @@ -14,12 +14,12 @@ .row { margin: 14px 0; display: flex; gap: 8px; align-items: center } .warn { color: #e0b050 } -

Hardware

+

Hardware

- + - +