initial migration, not vetted

This commit is contained in:
John Poole 2026-02-28 16:33:16 -08:00
commit 09bc6a5304
29 changed files with 122147 additions and 0 deletions

View file

@ -0,0 +1,176 @@
#!/usr/bin/env node
/**
* batch_render.js
* 20260228 ChatGPT
* $Header$
*
* Example:
* node batch_render.js job.json
*/
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import http from 'node:http';
import { pathToFileURL, fileURLToPath } from 'node:url';
import puppeteer from 'puppeteer';
function die (msg) { console.error(msg); process.exit(1); }
function ensureDir(p) { fs.mkdirSync(p, { recursive: true }); }
function listFiles(dir, suffix) {
const out = [];
for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, ent.name);
if (ent.isDirectory()) out.push(...listFiles(full, suffix));
else if (!suffix || ent.name.toLowerCase().endsWith(suffix)) out.push(full);
}
return out;
}
function sanitizeBasename(p) {
const b = path.basename(p);
return b.replace(/[^\w.\-]+/g, '_');
}
function mimeTypeFor(p) {
const ext = path.extname(p).toLowerCase();
if (ext === '.html') return 'text/html; charset=utf-8';
if (ext === '.js') return 'text/javascript; charset=utf-8';
if (ext === '.json') return 'application/json; charset=utf-8';
if (ext === '.glb') return 'model/gltf-binary';
if (ext === '.gltf') return 'model/gltf+json; charset=utf-8';
if (ext === '.bin') return 'application/octet-stream';
if (ext === '.png') return 'image/png';
if (ext === '.jpg' || ext === '.jpeg') return 'image/jpeg';
if (ext === '.css') return 'text/css; charset=utf-8';
return 'application/octet-stream';
}
// Serve static files from rootDir
function startStaticServer(rootDir) {
const server = http.createServer((req, res) => {
try {
const urlPath = decodeURIComponent((req.url || '/').split('?')[0]);
const safePath = path.normalize(urlPath).replace(/^(\.\.(\/|\\|$))+/, '');
const filePath = path.join(rootDir, safePath);
// Default route
const finalPath = fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()
? path.join(filePath, 'index.html')
: filePath;
if (!fs.existsSync(finalPath) || !fs.statSync(finalPath).isFile()) {
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('404\n');
return;
}
const data = fs.readFileSync(finalPath);
res.writeHead(200, { 'Content-Type': mimeTypeFor(finalPath) });
res.end(data);
} catch (e) {
res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end(`500\n${String(e)}\n`);
}
});
return new Promise((resolve) => {
server.listen(0, '127.0.0.1', () => {
const addr = server.address();
resolve({ server, port: addr.port });
});
});
}
async function main() {
const jobPath = process.argv[2];
if (!jobPath) die("Usage: node batch_render.js job.json");
const jobAbs = path.resolve(jobPath);
const jobDir = path.dirname(jobAbs);
const job = JSON.parse(fs.readFileSync(jobAbs, 'utf8'));
const profileAbs = path.resolve(jobDir, job.profile ?? die("job.profile missing"));
const inputDirAbs = path.resolve(jobDir, job.input_dir ?? die("job.input_dir missing"));
const outputDirAbs = path.resolve(jobDir, job.output_dir ?? die("job.output_dir missing"));
const pattern = (job.pattern ?? ".glb").toLowerCase();
ensureDir(outputDirAbs);
const glbs = listFiles(inputDirAbs, pattern);
if (glbs.length === 0) die(`No files ending with '${pattern}' found under ${inputDirAbs}`);
const renderHtmlAbs = path.resolve(jobDir, 'render.html');
if (!fs.existsSync(renderHtmlAbs)) die(`Missing render.html at ${renderHtmlAbs}`);
// Start local server rooted at jobDir so render.html, libs/, glbs/, profile json are reachable
const { server, port } = await startStaticServer(jobDir);
const base = `http://127.0.0.1:${port}`;
// Load profile for viewport sizing
const profile = JSON.parse(fs.readFileSync(profileAbs, 'utf8'));
const vw = profile.output?.width ?? 1024;
const vh = profile.output?.height ?? 768;
const dpr = profile.output?.pixelRatio ?? 1;
const browser = await puppeteer.launch({
headless: 'new',
args: [
'--no-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu',
'--use-gl=swiftshader',
'--enable-unsafe-swiftshader'
]
});
try {
const page = await browser.newPage();
// Log page-side failures clearly
page.on('console', (msg) => console.log('PAGE console:', msg.type(), msg.text()));
page.on('pageerror', (err) => console.log('PAGE error:', err));
page.on('requestfailed', (req) => console.log('REQ failed:', req.url(), req.failure()?.errorText));
await page.setViewport({ width: vw, height: vh, deviceScaleFactor: dpr });
// Make URLs relative to server root
const renderUrl = `${base}/render.html`;
const profileUrl = `${base}/${encodeURIComponent(path.relative(jobDir, profileAbs)).replace(/%2F/g,'/')}`;
for (const glbAbs of glbs) {
const rel = path.relative(jobDir, glbAbs);
const glbUrl = `${base}/${encodeURIComponent(rel).replace(/%2F/g,'/')}`;
const outName = sanitizeBasename(glbAbs).replace(/\.glb$/i, '.png');
const outAbs = path.join(outputDirAbs, outName);
const url = `${renderUrl}?glb=${encodeURIComponent(glbUrl)}&profile=${encodeURIComponent(profileUrl)}`;
// Dont use networkidle0 here; module imports + GLB load can keep network “busy”
await page.goto(url, { waitUntil: 'load' });
await page.waitForFunction(() => {
return window.__RENDER_STATUS__ && window.__RENDER_STATUS__.ready === true;
}, { timeout: 120000 }); // 2 minutes, GLBs can be chunky
const status = await page.evaluate(() => window.__RENDER_STATUS__);
if (!status || !status.pngDataUrl) {
throw new Error(`No pngDataUrl produced for ${glbAbs} (status=${JSON.stringify(status)})`);
}
const b64 = status.pngDataUrl.replace(/^data:image\/png;base64,/, '');
fs.writeFileSync(outAbs, Buffer.from(b64, 'base64'));
console.log(`WROTE\t${outAbs}`);
}
await page.close();
} finally {
await browser.close();
server.close();
}
}
main().catch((e) => { console.error("ERROR:", e); process.exit(2); });

View file

@ -0,0 +1,6 @@
{
"profile": "./three_profile_20260228_111725.json",
"input_dir": "./glbs",
"output_dir": "./pngs",
"pattern": ".glb"
}

View file

@ -0,0 +1 @@
../vendor/three/addons/loaders/GLTFLoader.js

View file

@ -0,0 +1 @@
../vendor/three/build/three.module.js

91
web/batch_glb_png/link_glbs.sh Executable file
View file

@ -0,0 +1,91 @@
#!/bin/sh
# 20260228 ChatGPT
# $Header$
#
# Copy/paste:
# cd ~/work/Voron/renderlab/web/batch_glb_png
# chmod +x link_glbs.sh
# ./link_glbs.sh /home/jlpoole/work/Voron/test1
#
# This creates symlinks under ./glbs/ pointing to every *.glb under ROOT.
set -eu
ROOT="${1:-}"
[ -n "$ROOT" ] || { echo "Usage: $0 /path/to/root" >&2; exit 2; }
[ -d "$ROOT" ] || { echo "ERROR: not a directory: $ROOT" >&2; exit 2; }
OUTDIR="./glbs"
mkdir -p "$OUTDIR"
ts="$(date +%Y%m%d_%H%M%S)"
log="link_glbs_${ts}.log"
# Make ROOT absolute and strip any trailing slash for consistent relpath math.
ROOT_ABS="$(cd "$ROOT" && pwd)"
ROOT_ABS="${ROOT_ABS%/}"
echo "ROOT: $ROOT_ABS" | tee "$log"
echo "OUTDIR: $(cd "$OUTDIR" && pwd)" | tee -a "$log"
echo "LOG: $log" | tee -a "$log"
echo "" | tee -a "$log"
# Sanitize: turn a path into a filesystem-safe basename.
# Example:
# Klicky-Probe/Printers/Voron/.../Dock_mount_fixed_v2.stl.glb
# becomes:
# Klicky-Probe__Printers__Voron__...__Dock_mount_fixed_v2.stl.glb
sanitize() {
# shellcheck disable=SC2001
echo "$1" \
| sed 's|^/||' \
| sed 's|/|__|g' \
| sed 's|[^A-Za-z0-9._-]|_|g'
}
count=0
skipped=0
collisions=0
# Use -print0 to handle spaces safely.
find "$ROOT_ABS" -type f -name '*.glb' -print0 \
| while IFS= read -r -d '' f; do
# Compute relative path under ROOT_ABS
rel="${f#$ROOT_ABS/}"
name="$(sanitize "$rel")"
linkpath="$OUTDIR/$name"
# If the link exists and points to the same target, skip quietly.
if [ -L "$linkpath" ]; then
target="$(readlink "$linkpath" || true)"
if [ "$target" = "$f" ]; then
echo "SKIP (already linked): $linkpath -> $f" >> "$log"
skipped=$((skipped+1))
continue
fi
fi
# If the name already exists but points elsewhere, disambiguate.
if [ -e "$linkpath" ]; then
i=1
base="$linkpath"
while [ -e "$linkpath" ]; do
linkpath="${base%.glb}_$i.glb"
i=$((i+1))
done
collisions=$((collisions+1))
fi
ln -s "$f" "$linkpath"
echo "LINK: $linkpath -> $f" >> "$log"
count=$((count+1))
done
# The while loop runs in a subshell in many /bin/sh implementations,
# so counters above may not update. Summarize by counting log lines instead.
linked_lines="$(grep -c '^LINK: ' "$log" 2>/dev/null || echo 0)"
skipped_lines="$(grep -c '^SKIP ' "$log" 2>/dev/null || echo 0)"
echo "" | tee -a "$log"
echo "DONE. linked=$linked_lines skipped=$skipped_lines (details in $log)" | tee -a "$log"

View file

@ -0,0 +1,135 @@
ROOT: /home/jlpoole/work/Voron/test1
OUTDIR: /home/jlpoole/work/Voron/renderlab/web/batch_glb_png/glbs
LOG: link_glbs_20260228_152056.log
LINK: ./glbs/Klicky-Probe__Printers__Voron__v1.8_v2.4_Legacy_Trident__v1.8_v2.4_Legacy_Trident_STL__Dock_mount_fixed_v2.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Printers/Voron/v1.8_v2.4_Legacy_Trident/v1.8_v2.4_Legacy_Trident_STL/Dock_mount_fixed_v2.stl.glb
LINK: ./glbs/Klicky-Probe__Printers__Voron__v1.8_v2.4_Legacy_Trident__v1.8_v2.4_Legacy_Trident_STL__Dock_sidemount_fixed_v2.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Printers/Voron/v1.8_v2.4_Legacy_Trident/v1.8_v2.4_Legacy_Trident_STL/Dock_sidemount_fixed_v2.stl.glb
LINK: ./glbs/Klicky-Probe__Printers__Voron__v1.8_v2.4_Legacy_Trident__v1.8_v2.4_Legacy_Trident_STL__Dock_sidemount_left_v2.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Printers/Voron/v1.8_v2.4_Legacy_Trident/v1.8_v2.4_Legacy_Trident_STL/Dock_sidemount_left_v2.stl.glb
LINK: ./glbs/Klicky-Probe__Printers__Voron__v1.8_v2.4_Legacy_Trident__v1.8_v2.4_Legacy_Trident_STL__Dock_sidemount_right_v2.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Printers/Voron/v1.8_v2.4_Legacy_Trident/v1.8_v2.4_Legacy_Trident_STL/Dock_sidemount_right_v2.stl.glb
LINK: ./glbs/Klicky-Probe__Printers__Voron__v1.8_v2.4_Legacy_Trident__v1.8_v2.4_Legacy_Trident_STL__KlickyProbe_AB_mount_v2.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Printers/Voron/v1.8_v2.4_Legacy_Trident/v1.8_v2.4_Legacy_Trident_STL/KlickyProbe_AB_mount_v2.stl.glb
LINK: ./glbs/Klicky-Probe__Printers__Voron__v1.8_v2.4_Legacy_Trident__v1.8_v2.4_Legacy_Trident_STL__Mount_magnet_holder.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Printers/Voron/v1.8_v2.4_Legacy_Trident/v1.8_v2.4_Legacy_Trident_STL/Mount_magnet_holder.stl.glb
LINK: ./glbs/Klicky-Probe__Printers__Voron__v1.8_v2.4_Legacy_Trident__v1.8_v2.4_Legacy_Trident_STL__Mount_magnet_pressfit_helper.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Printers/Voron/v1.8_v2.4_Legacy_Trident/v1.8_v2.4_Legacy_Trident_STL/Mount_magnet_pressfit_helper.stl.glb
LINK: ./glbs/Klicky-Probe__Printers__Voron__v1.8_v2.4_Legacy_Trident__v1.8_v2.4_Legacy_Trident_STL__Mount_pressfit_holder_v2.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Printers/Voron/v1.8_v2.4_Legacy_Trident/v1.8_v2.4_Legacy_Trident_STL/Mount_pressfit_holder_v2.stl.glb
LINK: ./glbs/Klicky-Probe__Probes__KlickyProbe__STL__1mm_Spacer.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Probes/KlickyProbe/STL/1mm_Spacer.stl.glb
LINK: ./glbs/Klicky-Probe__Probes__KlickyProbe__STL__KlickyProbe_v2.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Probes/KlickyProbe/STL/KlickyProbe_v2.stl.glb
LINK: ./glbs/Klicky-Probe__Probes__KlickyProbe__STL__Probe_Dock_v2.1.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Probes/KlickyProbe/STL/Probe_Dock_v2.1.stl.glb
LINK: ./glbs/Klicky-Probe__Probes__KlickyProbe__STL__Probe_magnet_holder.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Probes/KlickyProbe/STL/Probe_magnet_holder.stl.glb
LINK: ./glbs/Klicky-Probe__Probes__KlickyProbe__STL__Probe_magnet_pressfit_helper.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Probes/KlickyProbe/STL/Probe_magnet_pressfit_helper.stl.glb
LINK: ./glbs/Klicky-Probe__Probes__KlickyProbe__STL__Probe_pressfit_holder.stl.glb -> /home/jlpoole/work/Voron/test1/Klicky-Probe/Probes/KlickyProbe/STL/Probe_pressfit_holder.stl.glb
LINK: ./glbs/LDOVoron2__STLs__COB_Light_Strip__cob_light_strip_mount_100mm.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/COB Light Strip/cob_light_strip_mount_100mm.stl.glb
LINK: ./glbs/LDOVoron2__STLs__COB_Light_Strip__cob_light_strip_mount_150mm.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/COB Light Strip/cob_light_strip_mount_150mm.stl.glb
LINK: ./glbs/LDOVoron2__STLs__COB_Light_Strip__cob_light_strip_mount_50mm.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/COB Light Strip/cob_light_strip_mount_50mm.stl.glb
LINK: ./glbs/LDOVoron2__STLs__LDO_Door__handle_a_x2.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/LDO Door/handle_a_x2.stl.glb
LINK: ./glbs/LDOVoron2__STLs__LDO_Door__handle_b.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/LDO Door/handle_b.stl.glb
LINK: ./glbs/LDOVoron2__STLs__LDO_Door__handle_b_nameplate.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/LDO Door/handle_b_nameplate.stl.glb
LINK: ./glbs/LDOVoron2__STLs__LDO_Door__screw_hinge_x3.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/LDO Door/screw_hinge_x3.stl.glb
LINK: ./glbs/LDOVoron2__STLs__exhaust_cover.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/exhaust_cover.stl.glb
LINK: ./glbs/LDOVoron2__STLs__handlebar_spacer_x4.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/handlebar_spacer_x4.stl.glb
LINK: ./glbs/LDOVoron2__STLs__ldo_bestagon_insert.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/ldo_bestagon_insert.stl.glb
LINK: ./glbs/LDOVoron2__STLs__led_fan_pcb_spacer_x2.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/led_fan_pcb_spacer_x2.stl.glb
LINK: ./glbs/LDOVoron2__STLs__nozzle_probe_ldo.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/nozzle_probe_ldo.stl.glb
LINK: ./glbs/LDOVoron2__STLs__z_belt_cover_a_led.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/z_belt_cover_a_led.stl.glb
LINK: ./glbs/LDOVoron2__STLs__z_rail_stop_x4.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoron2/STLs/z_rail_stop_x4.stl.glb
LINK: ./glbs/LDOVoronTrident__STLs__BTT_Pi_TFT4.3_Mount___a__faceplate.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoronTrident/STLs/BTT Pi TFT4.3 Mount/[a]_faceplate.stl.glb
LINK: ./glbs/LDOVoronTrident__STLs__BTT_Pi_TFT4.3_Mount__mount.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoronTrident/STLs/BTT Pi TFT4.3 Mount/mount.stl.glb
LINK: ./glbs/LDOVoronTrident__STLs__bed_wago_mount.stl.glb -> /home/jlpoole/work/Voron/test1/LDOVoronTrident/STLs/bed_wago_mount.stl.glb
LINK: ./glbs/Leviathan__STLs__Leviathan_bracket_set.stl.glb -> /home/jlpoole/work/Voron/test1/Leviathan/STLs/Leviathan_bracket_set.stl.glb
LINK: ./glbs/Nitehawk-SB__STLs__cw2_captive_pcb_cover.stl.glb -> /home/jlpoole/work/Voron/test1/Nitehawk-SB/STLs/cw2_captive_pcb_cover.stl.glb
LINK: ./glbs/Nitehawk-SB__STLs__usb_adapter_mount.stl.glb -> /home/jlpoole/work/Voron/test1/Nitehawk-SB/STLs/usb_adapter_mount.stl.glb
LINK: ./glbs/Voron-2__STLs__Electronics_Bay__lrs_200_psu_bracket_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Electronics_Bay/lrs_200_psu_bracket_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Electronics_Bay__pcb_din_clip_x3.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Electronics_Bay/pcb_din_clip_x3.stl.glb
LINK: ./glbs/Voron-2__STLs__Electronics_Bay__wago_221-415_mount_3by5.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Electronics_Bay/wago_221-415_mount_3by5.stl.glb
LINK: ./glbs/Voron-2__STLs__Exhaust_Filter__exhaust_filter_grill.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Exhaust_Filter/exhaust_filter_grill.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__AB_Drive_Units___a__cable_cover.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/AB_Drive_Units/[a]_cable_cover.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__AB_Drive_Units___a__z_chain_retainer_bracket_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/AB_Drive_Units/[a]_z_chain_retainer_bracket_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__AB_Drive_Units__a_drive_frame_lower.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/AB_Drive_Units/a_drive_frame_lower.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__AB_Drive_Units__a_drive_frame_upper.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/AB_Drive_Units/a_drive_frame_upper.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__AB_Drive_Units__b_drive_frame_lower.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/AB_Drive_Units/b_drive_frame_lower.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__AB_Drive_Units__b_drive_frame_upper.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/AB_Drive_Units/b_drive_frame_upper.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__Front_Idlers___a__tensioner_left.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/Front_Idlers/[a]_tensioner_left.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__Front_Idlers___a__tensioner_right.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/Front_Idlers/[a]_tensioner_right.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__Front_Idlers__front_idler_left_lower.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/Front_Idlers/front_idler_left_lower.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__Front_Idlers__front_idler_left_upper.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/Front_Idlers/front_idler_left_upper.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__Front_Idlers__front_idler_right_lower.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/Front_Idlers/front_idler_right_lower.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__Front_Idlers__front_idler_right_upper.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/Front_Idlers/front_idler_right_upper.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__X_Axis__XY_Joints___a__endstop_pod_D2F_switch.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/X_Axis/XY_Joints/[a]_endstop_pod_D2F_switch.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__X_Axis__XY_Joints___a__xy_joint_cable_bridge_2hole.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/X_Axis/XY_Joints/[a]_xy_joint_cable_bridge_2hole.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__X_Axis__XY_Joints__xy_joint_left_lower_MGN12.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/X_Axis/XY_Joints/xy_joint_left_lower_MGN12.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__X_Axis__XY_Joints__xy_joint_left_upper_MGN12.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/X_Axis/XY_Joints/xy_joint_left_upper_MGN12.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__X_Axis__XY_Joints__xy_joint_right_lower_MGN12.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/X_Axis/XY_Joints/xy_joint_right_lower_MGN12.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__X_Axis__XY_Joints__xy_joint_right_upper_MGN12.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/X_Axis/XY_Joints/xy_joint_right_upper_MGN12.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__X_Axis__X_Carriage__probe_retainer_bracket.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/X_Axis/X_Carriage/probe_retainer_bracket.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__X_Axis__X_Carriage__x_frame_V2TR_MGN12_left.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/X_Axis/X_Carriage/x_frame_V2TR_MGN12_left.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__X_Axis__X_Carriage__x_frame_V2TR_MGN12_right.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/X_Axis/X_Carriage/x_frame_V2TR_MGN12_right.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__Z_Joints__z_joint_lower_x4.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/Z_Joints/z_joint_lower_x4.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__Z_Joints__z_joint_upper_x4.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/Z_Joints/z_joint_upper_x4.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry___a__z_belt_clip_lower_x4.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/[a]_z_belt_clip_lower_x4.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry___a__z_belt_clip_upper_x4.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/[a]_z_belt_clip_upper_x4.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__z_chain_bottom_anchor.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/z_chain_bottom_anchor.stl.glb
LINK: ./glbs/Voron-2__STLs__Gantry__z_chain_guide.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Gantry/z_chain_guide.stl.glb
LINK: ./glbs/Voron-2__STLs__Panel_Mounting__Front_Doors__latch_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Panel_Mounting/Front_Doors/latch_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Panel_Mounting__bottom_panel_clip_x4.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Panel_Mounting/bottom_panel_clip_x4.stl.glb
LINK: ./glbs/Voron-2__STLs__Panel_Mounting__bottom_panel_hinge_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Panel_Mounting/bottom_panel_hinge_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Panel_Mounting__corner_panel_clip_4mm_x8.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Panel_Mounting/corner_panel_clip_4mm_x8.stl.glb
LINK: ./glbs/Voron-2__STLs__Panel_Mounting__corner_panel_clip_6mm_x8.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Panel_Mounting/corner_panel_clip_6mm_x8.stl.glb
LINK: ./glbs/Voron-2__STLs__Panel_Mounting__deck_support_4mm_x8.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Panel_Mounting/deck_support_4mm_x8.stl.glb
LINK: ./glbs/Voron-2__STLs__Panel_Mounting__midspan_panel_clip_4mm_x7.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Panel_Mounting/midspan_panel_clip_4mm_x7.stl.glb
LINK: ./glbs/Voron-2__STLs__Panel_Mounting__midspan_panel_clip_6mm_x8.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Panel_Mounting/midspan_panel_clip_6mm_x8.stl.glb
LINK: ./glbs/Voron-2__STLs__Panel_Mounting__z_belt_cover_a_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Panel_Mounting/z_belt_cover_a_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Panel_Mounting__z_belt_cover_b_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Panel_Mounting/z_belt_cover_b_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__300__front_skirt_a_300.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/300/front_skirt_a_300.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__300__front_skirt_b_300.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/300/front_skirt_b_300.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__300__rear_center_skirt_300.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/300/rear_center_skirt_300.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__300__side_skirt_a_300_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/300/side_skirt_a_300_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__300__side_skirt_b_300_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/300/side_skirt_b_300_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__350__front_skirt_a_350.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/350/front_skirt_a_350.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__350__front_skirt_b_350.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/350/front_skirt_b_350.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__350__rear_center_skirt_350.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/350/rear_center_skirt_350.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__350__side_skirt_a_350_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/350/side_skirt_a_350_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__350__side_skirt_b_350_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/350/side_skirt_b_350_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts___a__belt_guard_a_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/[a]_belt_guard_a_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts___a__belt_guard_b_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/[a]_belt_guard_b_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts___a__fan_grill_a_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/[a]_fan_grill_a_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts___a__fan_grill_b_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/[a]_fan_grill_b_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts___a__fan_grill_open_optional_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/[a]_fan_grill_open_optional_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts___a__fan_grill_retainer_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/[a]_fan_grill_retainer_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts___a__keystone_blank_insert.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/[a]_keystone_blank_insert.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__keystone_panel.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/keystone_panel.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__power_inlet_IECGS_1mm.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/power_inlet_IECGS_1mm.stl.glb
LINK: ./glbs/Voron-2__STLs__Skirts__side_fan_support_x2.STL.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Skirts/side_fan_support_x2.STL.glb
LINK: ./glbs/Voron-2__STLs__Spool_Management__bowden_retainer.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Spool_Management/bowden_retainer.stl.glb
LINK: ./glbs/Voron-2__STLs__Spool_Management__spool_holder.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Spool_Management/spool_holder.stl.glb
LINK: ./glbs/Voron-2__STLs__Test_Prints__Heatset_Practice.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Test_Prints/Heatset_Practice.stl.glb
LINK: ./glbs/Voron-2__STLs__Tools__MGN12_rail_guide_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Tools/MGN12_rail_guide_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Tools__MGN9_rail_guide_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Tools/MGN9_rail_guide_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Tools__pulley_jig.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Tools/pulley_jig.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Drive___a__belt_tensioner_a_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Drive/[a]_belt_tensioner_a_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Drive___a__belt_tensioner_b_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Drive/[a]_belt_tensioner_b_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Drive___a__z_drive_baseplate_a_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Drive/[a]_z_drive_baseplate_a_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Drive___a__z_drive_baseplate_b_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Drive/[a]_z_drive_baseplate_b_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Drive__z_drive_main_a_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Drive/z_drive_main_a_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Drive__z_drive_main_b_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Drive/z_drive_main_b_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Drive__z_drive_retainer_a_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Drive/z_drive_retainer_a_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Drive__z_drive_retainer_b_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Drive/z_drive_retainer_b_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Drive__z_motor_mount_a_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Drive/z_motor_mount_a_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Drive__z_motor_mount_b_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Drive/z_motor_mount_b_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Idlers___a__z_tensioner_9mm_x4.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Idlers/[a]_z_tensioner_9mm_x4.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Idlers__z_tensioner_bracket_a_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Idlers/z_tensioner_bracket_a_x2.stl.glb
LINK: ./glbs/Voron-2__STLs__Z_Idlers__z_tensioner_bracket_b_x2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-2/STLs/Z_Idlers/z_tensioner_bracket_b_x2.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Clockwork2__Direct_Drive___a__guidler_a.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Clockwork2/Direct_Drive/[a]_guidler_a.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Clockwork2__Direct_Drive___a__guidler_b.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Clockwork2/Direct_Drive/[a]_guidler_b.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Clockwork2__Direct_Drive___a__latch.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Clockwork2/Direct_Drive/[a]_latch.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Clockwork2__Direct_Drive___a__latch_shuttle.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Clockwork2/Direct_Drive/[a]_latch_shuttle.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Clockwork2__Direct_Drive__main_body.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Clockwork2/Direct_Drive/main_body.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Clockwork2__Direct_Drive__motor_plate.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Clockwork2/Direct_Drive/motor_plate.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Clockwork2___a__pcb_spacer.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Clockwork2/[a]_pcb_spacer.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Clockwork2__chain_anchor_2hole.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Clockwork2/chain_anchor_2hole.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Stealthburner__Printheads__revo_voron__stealthburner_printhead_revo_voron_front.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Stealthburner/Printheads/revo_voron/stealthburner_printhead_revo_voron_front.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Stealthburner__Printheads__revo_voron__stealthburner_printhead_revo_voron_rear_cw2.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Stealthburner/Printheads/revo_voron/stealthburner_printhead_revo_voron_rear_cw2.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Stealthburner___a__stealthburner_main_body.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Stealthburner/[a]_stealthburner_main_body.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Stealthburner___c__stealthburner_LED_diffuser.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Stealthburner/[c]_stealthburner_LED_diffuser.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Stealthburner___o__stealthburner_LED_carrier.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Stealthburner/[o]_stealthburner_LED_carrier.stl.glb
LINK: ./glbs/Voron-Stealthburner__STLs__Stealthburner___o__stealthburner_LED_diffuser_mask.stl.glb -> /home/jlpoole/work/Voron/test1/Voron-Stealthburner/STLs/Stealthburner/[o]_stealthburner_LED_diffuser_mask.stl.glb
DONE. linked=128 skipped=0
0 (details in link_glbs_20260228_152056.log)

5
web/batch_glb_png/node_modules/.gitignore generated vendored Normal file
View file

@ -0,0 +1,5 @@
.bin
.modules.yaml
.pnpm
.pnpm-workspace-state-v1.json
puppeteer

View file

@ -0,0 +1,7 @@
{
"name": "batch_glb_png",
"type": "module",
"dependencies": {
"puppeteer": "^24.37.5"
}
}

868
web/batch_glb_png/pnpm-lock.yaml generated Normal file
View file

@ -0,0 +1,868 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
puppeteer:
specifier: ^24.37.5
version: 24.37.5
packages:
'@babel/code-frame@7.29.0':
resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@7.28.5':
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
engines: {node: '>=6.9.0'}
'@puppeteer/browsers@2.13.0':
resolution: {integrity: sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==}
engines: {node: '>=18'}
hasBin: true
'@tootallnate/quickjs-emscripten@0.23.0':
resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==}
'@types/node@25.3.2':
resolution: {integrity: sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q==}
'@types/yauzl@2.10.3':
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
agent-base@7.1.4:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
ast-types@0.13.4:
resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
engines: {node: '>=4'}
b4a@1.8.0:
resolution: {integrity: sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==}
peerDependencies:
react-native-b4a: '*'
peerDependenciesMeta:
react-native-b4a:
optional: true
bare-events@2.8.2:
resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==}
peerDependencies:
bare-abort-controller: '*'
peerDependenciesMeta:
bare-abort-controller:
optional: true
bare-fs@4.5.5:
resolution: {integrity: sha512-XvwYM6VZqKoqDll8BmSww5luA5eflDzY0uEFfBJtFKe4PAAtxBjU3YIxzIBzhyaEQBy1VXEQBto4cpN5RZJw+w==}
engines: {bare: '>=1.16.0'}
peerDependencies:
bare-buffer: '*'
peerDependenciesMeta:
bare-buffer:
optional: true
bare-os@3.7.0:
resolution: {integrity: sha512-64Rcwj8qlnTZU8Ps6JJEdSmxBEUGgI7g8l+lMtsJLl4IsfTcHMTfJ188u2iGV6P6YPRZrtv72B2kjn+hp+Yv3g==}
engines: {bare: '>=1.14.0'}
bare-path@3.0.0:
resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==}
bare-stream@2.8.0:
resolution: {integrity: sha512-reUN0M2sHRqCdG4lUK3Fw8w98eeUIZHL5c3H7Mbhk2yVBL+oofgaIp0ieLfD5QXwPCypBpmEEKU2WZKzbAk8GA==}
peerDependencies:
bare-buffer: '*'
bare-events: '*'
peerDependenciesMeta:
bare-buffer:
optional: true
bare-events:
optional: true
bare-url@2.3.2:
resolution: {integrity: sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==}
basic-ftp@5.2.0:
resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==}
engines: {node: '>=10.0.0'}
buffer-crc32@0.2.13:
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
chromium-bidi@14.0.0:
resolution: {integrity: sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==}
peerDependencies:
devtools-protocol: '*'
cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
cosmiconfig@9.0.0:
resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==}
engines: {node: '>=14'}
peerDependencies:
typescript: '>=4.9.5'
peerDependenciesMeta:
typescript:
optional: true
data-uri-to-buffer@6.0.2:
resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==}
engines: {node: '>= 14'}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
degenerator@5.0.1:
resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==}
engines: {node: '>= 14'}
devtools-protocol@0.0.1566079:
resolution: {integrity: sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
end-of-stream@1.4.5:
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
error-ex@1.3.4:
resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
escodegen@2.1.0:
resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
engines: {node: '>=6.0'}
hasBin: true
esprima@4.0.1:
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
engines: {node: '>=4'}
hasBin: true
estraverse@5.3.0:
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
engines: {node: '>=4.0'}
esutils@2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
events-universal@1.0.1:
resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==}
extract-zip@2.0.1:
resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
engines: {node: '>= 10.17.0'}
hasBin: true
fast-fifo@1.3.2:
resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
fd-slicer@1.1.0:
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
get-stream@5.2.0:
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
engines: {node: '>=8'}
get-uri@6.0.5:
resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==}
engines: {node: '>= 14'}
http-proxy-agent@7.0.2:
resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
engines: {node: '>= 14'}
https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
ip-address@10.1.0:
resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==}
engines: {node: '>= 12'}
is-arrayish@0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
js-yaml@4.1.1:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
lru-cache@7.18.3:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
netmask@2.0.2:
resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==}
engines: {node: '>= 0.4.0'}
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
pac-proxy-agent@7.2.0:
resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==}
engines: {node: '>= 14'}
pac-resolver@7.0.1:
resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==}
engines: {node: '>= 14'}
parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
parse-json@5.2.0:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
pend@1.2.0:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
progress@2.0.3:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'}
proxy-agent@6.5.0:
resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==}
engines: {node: '>= 14'}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
pump@3.0.4:
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
puppeteer-core@24.37.5:
resolution: {integrity: sha512-ybL7iE78YPN4T6J+sPLO7r0lSByp/0NN6PvfBEql219cOnttoTFzCWKiBOjstXSqi/OKpwae623DWAsL7cn2MQ==}
engines: {node: '>=18'}
puppeteer@24.37.5:
resolution: {integrity: sha512-3PAOIQLceyEmn1Fi76GkGO2EVxztv5OtdlB1m8hMUZL3f8KDHnlvXbvCXv+Ls7KzF1R0KdKBqLuT/Hhrok12hQ==}
engines: {node: '>=18'}
hasBin: true
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
semver@7.7.4:
resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
engines: {node: '>=10'}
hasBin: true
smart-buffer@4.2.0:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
socks-proxy-agent@8.0.5:
resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==}
engines: {node: '>= 14'}
socks@2.8.7:
resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==}
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
streamx@2.23.0:
resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
tar-fs@3.1.1:
resolution: {integrity: sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==}
tar-stream@3.1.8:
resolution: {integrity: sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==}
teex@1.0.1:
resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==}
text-decoder@1.2.7:
resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
typed-query-selector@2.12.1:
resolution: {integrity: sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==}
undici-types@7.18.2:
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
webdriver-bidi-protocol@0.4.1:
resolution: {integrity: sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==}
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
ws@8.19.0:
resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
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
y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
yauzl@2.10.0:
resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
snapshots:
'@babel/code-frame@7.29.0':
dependencies:
'@babel/helper-validator-identifier': 7.28.5
js-tokens: 4.0.0
picocolors: 1.1.1
'@babel/helper-validator-identifier@7.28.5': {}
'@puppeteer/browsers@2.13.0':
dependencies:
debug: 4.4.3
extract-zip: 2.0.1
progress: 2.0.3
proxy-agent: 6.5.0
semver: 7.7.4
tar-fs: 3.1.1
yargs: 17.7.2
transitivePeerDependencies:
- bare-abort-controller
- bare-buffer
- react-native-b4a
- supports-color
'@tootallnate/quickjs-emscripten@0.23.0': {}
'@types/node@25.3.2':
dependencies:
undici-types: 7.18.2
optional: true
'@types/yauzl@2.10.3':
dependencies:
'@types/node': 25.3.2
optional: true
agent-base@7.1.4: {}
ansi-regex@5.0.1: {}
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
argparse@2.0.1: {}
ast-types@0.13.4:
dependencies:
tslib: 2.8.1
b4a@1.8.0: {}
bare-events@2.8.2: {}
bare-fs@4.5.5:
dependencies:
bare-events: 2.8.2
bare-path: 3.0.0
bare-stream: 2.8.0(bare-events@2.8.2)
bare-url: 2.3.2
fast-fifo: 1.3.2
transitivePeerDependencies:
- bare-abort-controller
- react-native-b4a
bare-os@3.7.0: {}
bare-path@3.0.0:
dependencies:
bare-os: 3.7.0
bare-stream@2.8.0(bare-events@2.8.2):
dependencies:
streamx: 2.23.0
teex: 1.0.1
optionalDependencies:
bare-events: 2.8.2
transitivePeerDependencies:
- bare-abort-controller
- react-native-b4a
bare-url@2.3.2:
dependencies:
bare-path: 3.0.0
basic-ftp@5.2.0: {}
buffer-crc32@0.2.13: {}
callsites@3.1.0: {}
chromium-bidi@14.0.0(devtools-protocol@0.0.1566079):
dependencies:
devtools-protocol: 0.0.1566079
mitt: 3.0.1
zod: 3.25.76
cliui@8.0.1:
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
color-name@1.1.4: {}
cosmiconfig@9.0.0:
dependencies:
env-paths: 2.2.1
import-fresh: 3.3.1
js-yaml: 4.1.1
parse-json: 5.2.0
data-uri-to-buffer@6.0.2: {}
debug@4.4.3:
dependencies:
ms: 2.1.3
degenerator@5.0.1:
dependencies:
ast-types: 0.13.4
escodegen: 2.1.0
esprima: 4.0.1
devtools-protocol@0.0.1566079: {}
emoji-regex@8.0.0: {}
end-of-stream@1.4.5:
dependencies:
once: 1.4.0
env-paths@2.2.1: {}
error-ex@1.3.4:
dependencies:
is-arrayish: 0.2.1
escalade@3.2.0: {}
escodegen@2.1.0:
dependencies:
esprima: 4.0.1
estraverse: 5.3.0
esutils: 2.0.3
optionalDependencies:
source-map: 0.6.1
esprima@4.0.1: {}
estraverse@5.3.0: {}
esutils@2.0.3: {}
events-universal@1.0.1:
dependencies:
bare-events: 2.8.2
transitivePeerDependencies:
- bare-abort-controller
extract-zip@2.0.1:
dependencies:
debug: 4.4.3
get-stream: 5.2.0
yauzl: 2.10.0
optionalDependencies:
'@types/yauzl': 2.10.3
transitivePeerDependencies:
- supports-color
fast-fifo@1.3.2: {}
fd-slicer@1.1.0:
dependencies:
pend: 1.2.0
get-caller-file@2.0.5: {}
get-stream@5.2.0:
dependencies:
pump: 3.0.4
get-uri@6.0.5:
dependencies:
basic-ftp: 5.2.0
data-uri-to-buffer: 6.0.2
debug: 4.4.3
transitivePeerDependencies:
- supports-color
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.4
debug: 4.4.3
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.4
debug: 4.4.3
transitivePeerDependencies:
- supports-color
import-fresh@3.3.1:
dependencies:
parent-module: 1.0.1
resolve-from: 4.0.0
ip-address@10.1.0: {}
is-arrayish@0.2.1: {}
is-fullwidth-code-point@3.0.0: {}
js-tokens@4.0.0: {}
js-yaml@4.1.1:
dependencies:
argparse: 2.0.1
json-parse-even-better-errors@2.3.1: {}
lines-and-columns@1.2.4: {}
lru-cache@7.18.3: {}
mitt@3.0.1: {}
ms@2.1.3: {}
netmask@2.0.2: {}
once@1.4.0:
dependencies:
wrappy: 1.0.2
pac-proxy-agent@7.2.0:
dependencies:
'@tootallnate/quickjs-emscripten': 0.23.0
agent-base: 7.1.4
debug: 4.4.3
get-uri: 6.0.5
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6
pac-resolver: 7.0.1
socks-proxy-agent: 8.0.5
transitivePeerDependencies:
- supports-color
pac-resolver@7.0.1:
dependencies:
degenerator: 5.0.1
netmask: 2.0.2
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
parse-json@5.2.0:
dependencies:
'@babel/code-frame': 7.29.0
error-ex: 1.3.4
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
pend@1.2.0: {}
picocolors@1.1.1: {}
progress@2.0.3: {}
proxy-agent@6.5.0:
dependencies:
agent-base: 7.1.4
debug: 4.4.3
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6
lru-cache: 7.18.3
pac-proxy-agent: 7.2.0
proxy-from-env: 1.1.0
socks-proxy-agent: 8.0.5
transitivePeerDependencies:
- supports-color
proxy-from-env@1.1.0: {}
pump@3.0.4:
dependencies:
end-of-stream: 1.4.5
once: 1.4.0
puppeteer-core@24.37.5:
dependencies:
'@puppeteer/browsers': 2.13.0
chromium-bidi: 14.0.0(devtools-protocol@0.0.1566079)
debug: 4.4.3
devtools-protocol: 0.0.1566079
typed-query-selector: 2.12.1
webdriver-bidi-protocol: 0.4.1
ws: 8.19.0
transitivePeerDependencies:
- bare-abort-controller
- bare-buffer
- bufferutil
- react-native-b4a
- supports-color
- utf-8-validate
puppeteer@24.37.5:
dependencies:
'@puppeteer/browsers': 2.13.0
chromium-bidi: 14.0.0(devtools-protocol@0.0.1566079)
cosmiconfig: 9.0.0
devtools-protocol: 0.0.1566079
puppeteer-core: 24.37.5
typed-query-selector: 2.12.1
transitivePeerDependencies:
- bare-abort-controller
- bare-buffer
- bufferutil
- react-native-b4a
- supports-color
- typescript
- utf-8-validate
require-directory@2.1.1: {}
resolve-from@4.0.0: {}
semver@7.7.4: {}
smart-buffer@4.2.0: {}
socks-proxy-agent@8.0.5:
dependencies:
agent-base: 7.1.4
debug: 4.4.3
socks: 2.8.7
transitivePeerDependencies:
- supports-color
socks@2.8.7:
dependencies:
ip-address: 10.1.0
smart-buffer: 4.2.0
source-map@0.6.1:
optional: true
streamx@2.23.0:
dependencies:
events-universal: 1.0.1
fast-fifo: 1.3.2
text-decoder: 1.2.7
transitivePeerDependencies:
- bare-abort-controller
- react-native-b4a
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
strip-ansi@6.0.1:
dependencies:
ansi-regex: 5.0.1
tar-fs@3.1.1:
dependencies:
pump: 3.0.4
tar-stream: 3.1.8
optionalDependencies:
bare-fs: 4.5.5
bare-path: 3.0.0
transitivePeerDependencies:
- bare-abort-controller
- bare-buffer
- react-native-b4a
tar-stream@3.1.8:
dependencies:
b4a: 1.8.0
bare-fs: 4.5.5
fast-fifo: 1.3.2
streamx: 2.23.0
transitivePeerDependencies:
- bare-abort-controller
- bare-buffer
- react-native-b4a
teex@1.0.1:
dependencies:
streamx: 2.23.0
transitivePeerDependencies:
- bare-abort-controller
- react-native-b4a
text-decoder@1.2.7:
dependencies:
b4a: 1.8.0
transitivePeerDependencies:
- react-native-b4a
tslib@2.8.1: {}
typed-query-selector@2.12.1: {}
undici-types@7.18.2:
optional: true
webdriver-bidi-protocol@0.4.1: {}
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
wrappy@1.0.2: {}
ws@8.19.0: {}
y18n@5.0.8: {}
yargs-parser@21.1.1: {}
yargs@17.7.2:
dependencies:
cliui: 8.0.1
escalade: 3.2.0
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1
yauzl@2.10.0:
dependencies:
buffer-crc32: 0.2.13
fd-slicer: 1.1.0
zod@3.25.76: {}

View file

@ -0,0 +1,2 @@
onlyBuiltDependencies:
- puppeteer

View file

@ -0,0 +1,153 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Headless GLB Renderer</title>
<style>
html, body { margin:0; padding:0; background:#fff; overflow:hidden; }
canvas { display:block; }
</style>
<script type="importmap">
{
"imports": {
"three": "./libs/three.module.js",
"three/addons/": "./vendor/three/addons/"
}
}
</script>
</head>
<body>
<script type="module">
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
function qs (k) { return new URLSearchParams(location.search).get(k); }
const glbUrl = qs('glb');
const profileUrl = qs('profile');
window.__RENDER_STATUS__ = { ready:false, error:null, pngDataUrl:null };
if (!glbUrl || !profileUrl) {
window.__RENDER_STATUS__.error = "Missing ?glb=... or ?profile=...";
throw new Error(window.__RENDER_STATUS__.error);
}
const profile = await (await fetch(profileUrl)).json();
// Renderer
const renderer = new THREE.WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true,
alpha: false
});
renderer.setPixelRatio(profile.output?.pixelRatio ?? 1);
renderer.setSize(profile.output?.width ?? 1024, profile.output?.height ?? 768, false);
// Three r152+ color management (safe-guard)
if (THREE.SRGBColorSpace) renderer.outputColorSpace = THREE.SRGBColorSpace;
const bg = profile.scene?.background ?? 0xffffff;
renderer.setClearColor(bg, 1);
document.body.appendChild(renderer.domElement);
// Scene
const scene = new THREE.Scene();
// Camera
const cam = new THREE.PerspectiveCamera(
profile.camera?.fov ?? 50,
(profile.output?.width ?? 1024) / (profile.output?.height ?? 768),
profile.camera?.near ?? 0.1,
profile.camera?.far ?? 1000
);
cam.position.fromArray(profile.camera?.position ?? [0,0,10]);
cam.up.fromArray(profile.camera?.up ?? [0,1,0]);
// We wont use OrbitControls in headless; we just mimic its target by looking at it.
const target = new THREE.Vector3().fromArray(profile.controls?.target ?? [0,0,0]);
cam.lookAt(target);
cam.updateProjectionMatrix();
// Lights
const dir = new THREE.DirectionalLight(0xffffff, profile.lights?.directional?.intensity ?? 2.0);
dir.position.fromArray(profile.lights?.directional?.position ?? [5,5,5]);
scene.add(dir);
const amb = new THREE.AmbientLight(0xffffff, profile.lights?.ambient?.intensity ?? 0.5);
scene.add(amb);
// Load GLB
const loader = new GLTFLoader();
const gltf = await loader.loadAsync(glbUrl);
const root = gltf.scene;
scene.add(root);
// --- Fit-to-frame while keeping the profile's viewing direction ---
const box = new THREE.Box3().setFromObject(root);
const size = box.getSize(new THREE.Vector3());
const center = box.getCenter(new THREE.Vector3());
// Use profile target if present, but still re-center model so target makes sense
const profTarget = new THREE.Vector3().fromArray(profile.controls?.target ?? [0, 0, 0]);
// Shift the model so its center sits at the profile target (usually [0,0,0] or your saved target)
root.position.sub(center).add(profTarget);
// Recompute bounds after shifting
const box2 = new THREE.Box3().setFromObject(root);
const size2 = box2.getSize(new THREE.Vector3());
const center2 = box2.getCenter(new THREE.Vector3());
// Keep the same view direction as the profile camera->target vector
const profCamPos = new THREE.Vector3().fromArray(profile.camera?.position ?? [0, 0, 10]);
const viewDir = profCamPos.clone().sub(profTarget).normalize();
// Compute distance so the whole object fits the camera FOV
const maxDim = Math.max(size2.x, size2.y, size2.z, 1e-6);
const fov = cam.fov * (Math.PI / 180);
const fitPadding = profile.camera?.fitPadding ?? 1.15; // you can add this to JSON later
const distance = (maxDim / 2) / Math.tan(fov / 2) * fitPadding;
// Position camera and aim
cam.position.copy(center2).add(viewDir.multiplyScalar(distance));
cam.lookAt(center2);
cam.updateProjectionMatrix();
// Adjust near/far safely
cam.near = Math.max(0.01, distance / 100);
cam.far = distance * 100;
cam.updateProjectionMatrix();
// Apply renderParams
const wireframe = !!profile.renderParams?.wireframe;
root.traverse((obj) => {
if (obj.isMesh && obj.material) {
// If multi-material
if (Array.isArray(obj.material)) {
obj.material.forEach(m => { m.wireframe = wireframe; m.needsUpdate = true; });
} else {
obj.material.wireframe = wireframe;
obj.material.needsUpdate = true;
}
}
});
// Optional: edges overlay if you later add it (kept off unless you implement lines).
// profile.renderParams.edgeAngle is available; current lab.html uses it to build edges.
renderer.render(scene, cam);
// Give the GPU a beat (helps on some headless setups)
await new Promise(r => setTimeout(r, 50));
window.__RENDER_STATUS__.pngDataUrl = renderer.domElement.toDataURL('image/png');
window.__RENDER_STATUS__.ready = true;
</script>
</body>
</html>

View file

@ -0,0 +1,52 @@
{
"provenance": "lab.html exportProfile Sat Feb 28 2026 11:17:25 GMT-0800 (Pacific Standard Time)",
"output": {
"width": 1227,
"height": 994,
"pixelRatio": 1
},
"scene": {
"background": 16777215
},
"camera": {
"type": "PerspectiveCamera",
"fov": 50,
"near": 0.1,
"far": 1000,
"position": [
-12.932153617745264,
18.85116776875012,
19.470685732446455
],
"up": [
0,
1,
0
]
},
"controls": {
"target": [
0,
0,
0
]
},
"renderParams": {
"wireframe": false,
"edgeAngle": 30,
"lightIntensity": 3
},
"lights": {
"directional": {
"position": [
5,
5,
5
],
"intensity": 3
},
"ambient": {
"intensity": 0.6
}
}
}

1
web/batch_glb_png/vendor Symbolic link
View file

@ -0,0 +1 @@
../vendor