Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@resciencelab/shader-cli

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@resciencelab/shader-cli - npm Package Compare versions

Comparing version
0.1.1
to
0.1.2
+39
-64
dist/commands/export.js

@@ -12,2 +12,7 @@ import { success, error } from "../utils/output.js";

}
function buildRuntimeUrl(baseUrl, projectJson) {
const encoded = Buffer.from(projectJson).toString("base64");
const sep = baseUrl.includes("?") ? "&" : "?";
return `${baseUrl}${sep}project=${encodeURIComponent(encoded)}&autoplay=true`;
}
async function loadPlaywright() {

@@ -32,2 +37,3 @@ try {

headless: false,
channel: "chrome",
args: [

@@ -37,4 +43,2 @@ "--enable-unsafe-webgpu",

"--use-angle=metal",
"--window-position=-2400,-2400",
"--window-size=1,1",
],

@@ -44,4 +48,9 @@ });

try {
const context = await browser.newContext({ acceptDownloads: true });
const context = await browser.newContext({
acceptDownloads: true,
viewport: { width: 1920, height: 1080 },
});
const page = await context.newPage();
const project = JSON.parse(projectJson);
const layerCount = project.layers?.length ?? 0;
let hostname;

@@ -54,48 +63,19 @@ try {

}
const sp3 = new Spinner(`Loading ${hostname}`).spin();
await page.goto(runtimeUrl, { waitUntil: "networkidle" });
await page.waitForTimeout(3000);
sp3.succeed("Runtime loaded");
const project = JSON.parse(projectJson);
const layerCount = project.layers?.length ?? 0;
const sp4 = new Spinner(`Injecting project (${layerCount} layers, ${opts.duration}s)`).spin();
await page.evaluate((json) => {
const projectFile = JSON.parse(json);
const { useLayerStore, useTimelineStore, useEditorStore } = window.__SHADER_LAB_STORES__ ?? {};
if (useLayerStore) {
useLayerStore.getState().replaceState(projectFile.layers, projectFile.selectedLayerId, null);
}
if (useTimelineStore) {
useTimelineStore.getState().replaceState({
currentTime: 0,
duration: projectFile.timeline.duration,
isPlaying: true,
loop: projectFile.timeline.loop,
selectedKeyframeId: null,
selectedTrackId: null,
tracks: projectFile.timeline.tracks,
});
}
if (useEditorStore && projectFile.sceneConfig) {
useEditorStore.getState().updateSceneConfig(projectFile.sceneConfig);
useEditorStore.getState().setOutputSize(projectFile.composition.width, projectFile.composition.height);
}
}, projectJson);
await page.waitForTimeout(2000);
sp4.succeed("Project injected");
const sp3 = new Spinner(`Loading ${hostname} (${layerCount} layers)`).spin();
const fullUrl = buildRuntimeUrl(runtimeUrl, projectJson);
await page.goto(fullUrl, { waitUntil: "networkidle" });
// Wait for URL params loader to apply project (1.5s delay in component + render time)
await page.waitForTimeout(5000);
sp3.succeed("Runtime loaded & project injected");
if (opts.format === "png") {
const sp5 = new Spinner("Exporting PNG").spin();
// Open export dialog using Playwright locator
await page.locator('button[aria-label="Export"]').first().click();
await page.waitForTimeout(1500);
const downloadPromise = page.waitForEvent("download", { timeout: 30000 });
await page.evaluate((_time) => {
const exportBtn = document.querySelector('button[aria-label="Export"]');
exportBtn?.click();
setTimeout(() => {
const imgTab = [...document.querySelectorAll("button")].find(b => b.textContent?.trim() === "image");
imgTab?.click();
setTimeout(() => {
const exportPng = [...document.querySelectorAll("button")].find(b => b.textContent?.includes("Export PNG"));
exportPng?.click();
}, 500);
}, 1000);
}, opts.time);
// Click image tab (default, but ensure)
await page.getByRole("button", { name: "image", exact: true }).click();
await page.waitForTimeout(500);
// Click Export PNG
await page.getByRole("button", { name: "Export PNG" }).click();
const download = await downloadPromise;

@@ -112,21 +92,16 @@ sp5.succeed("PNG captured");

const spRec = new Spinner(`Recording ${formatLabel} (${opts.duration}s @ ${opts.fps}fps)`).spin();
// Open export dialog
await page.locator('button[aria-label="Export"]').first().click();
await page.waitForTimeout(1500);
const downloadPromise = page.waitForEvent("download", { timeout: 120000 });
await page.evaluate((format) => {
const exportBtn = document.querySelector('button[aria-label="Export"]');
exportBtn?.click();
setTimeout(() => {
const videoTab = [...document.querySelectorAll("button")].find(b => b.textContent?.trim() === "video");
videoTab?.click();
setTimeout(() => {
if (format === "mp4") {
const mp4Btn = [...document.querySelectorAll("button")].find(b => b.textContent?.trim() === "MP4");
mp4Btn?.click();
}
setTimeout(() => {
const btn = [...document.querySelectorAll("button")].find(b => b.textContent?.includes("Export WEBM") || b.textContent?.includes("Export MP4"));
btn?.click();
}, 500);
}, 500);
}, 1000);
}, opts.format);
// Click video tab
await page.getByRole("button", { name: "video", exact: true }).click();
await page.waitForTimeout(500);
// Select format if MP4
if (opts.format === "mp4") {
await page.getByRole("button", { name: "MP4", exact: true }).click();
await page.waitForTimeout(300);
}
// Click Export WEBM / Export MP4
await page.getByRole("button", { name: `Export ${formatLabel}` }).click();
const recStart = Date.now();

@@ -133,0 +108,0 @@ const tick = setInterval(() => pb.update(Date.now() - recStart), 200);

@@ -18,3 +18,3 @@ #!/usr/bin/env node

.description("Agent-native CLI for Shader Lab — compose and export WebGPU shader scenes from the terminal")
.version("0.1.1")
.version("0.1.2")
.option("--json", "Output as JSON (agent-friendly)")

@@ -21,0 +21,0 @@ .option("--project <path>", "Open a .lab project file")

{
"name": "@resciencelab/shader-cli",
"version": "0.1.1",
"version": "0.1.2",
"description": "Agent-native CLI for Shader Lab — compose and export WebGPU shader scenes from the terminal",

@@ -5,0 +5,0 @@ "type": "module",